This post was featured on the R Weekly highlights podcast hosted by Eric Nantz and Mike Thomas.
Following last week’s post on my testing workflow enhancements, Jenny Bryan kindly reminded me of the existence of an actual reset button when you’ve been interactively running tests that include some “local mess”: withr::local_envvar()
, withr::local_dir()
, usethis::local_project()
… The reset button is withr::deferred_run()
.
It is documented in Jenny’s article about test fixtures:
Since the global environment isn’t perishable, like a test environment is, you have to call deferred_run() explicitly to execute the deferred events. You can also clear them, without running, with deferred_clear().
It is also mentioned in messages from withr.
For some reason it hadn’t clicked for me until now?! But now I know better!
Example: making a withr::local_
mess and wiping it with withr::deferred_run()
Imagine a test file1
test_that("My function works", {
temp_dir <- withr::local_tempdir()
withr::local_options(blop = TRUE)
usethis::local_project(temp_dir, force = TRUE)
withr::local_envvar("TEST_SWITCH" = "something")
expect_something(my_function()) # not actual test code, you get the idea
})
Imagine the test fails and I run this in my R session probably after throwing a browser()
2 somewhere and running devtools::load_all()
:
temp_dir <- withr::local_tempdir()
withr::local_options(blop = FALSE)
usethis::local_project(temp_dir, force = TRUE)
#> ✔ Setting active project to '/tmp/RtmpTxY51U/file1dbe2032be39'
withr::local_envvar("TEST_SWITCH" = "something")
Then I do the actual debugging. At the end my session is all messy!
Sys.getenv("TEST_SWITCH")
#> [1] "something"
getOption("blop")
#> [1] FALSE
usethis::proj_sitrep() # cool function!
#> • working_directory: '/home/maelle/Documents/blog/simplymaelle/content/post/2023-10-16-deferred-run'
#> • active_usethis_proj: '/tmp/RtmpTxY51U/file1dbe2032be39'
#> • active_rstudio_proj: <unset>
#> • Your working directory is not the same as the active usethis project.
#> Set working directory to the project: `setwd(proj_get())`
#> Set project to working directory: `proj_set(getwd())`
Instead of fixing each thing by hand I can run
withr::deferred_run()
And now all is right in my session again, you’ll have to believe me or run the example yourself: indeed, demonstrating all of this in a knitr document makes it a bit more challenging. I swear that outside of a knitr document, this all works even for code that changes the current directory!
Sys.getenv("TEST_SWITCH")
getOption("blop")
usethis::proj_get()
So I can go and debug the next failing test. 😸
What about rlang’s local mess?
rlang itself has rlang::local_interactive()
and rlang::local_options()
. Luckily they are reset by withr::deferred_run()
.
I’m not sure exactly where the compatibility comes from, maybe from the standalone defer script. In any case, that’s good news!
Conclusion
So, in summary, if you’re running test code interactively to debug them, and this test code changed some things using withr::local_
or other compatible local_
functions, to get your session back to its initial state without restarting R, run withr::deferred_run()
! Thanks Jenny for the reminder!
-
Test switches are sooo handy. ↩︎
-
For better debugging advice, refer to Shannon Pileggi’s materials! ↩︎