/
index.Rmd
88 lines (60 loc) · 3.54 KB
/
index.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
---
title: "The real reset button for local mess fom tests: withr::deferred_run()"
date: '2023-10-16'
slug: test-local-mess-reset
output: hugodown::hugo_document
---
*This post was featured on the [R Weekly highlights podcast](https://rweekly.fireside.fm/141) hosted by Eric Nantz and Mike Thomas.*
Following [last week's post on my testing workflow enhancements](/2023/10/09/test-workflow-enhancement/), 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](https://testthat.r-lib.org/articles/test-fixtures.html):
> 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](https://github.com/r-lib/withr/blob/0c5254ebc74590e80cc0056303d74b049b943920/R/defer.R#L152).
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 file[^switch]
```r
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
})
```
[^switch]: [Test switches](https://blog.r-hub.io/2023/01/23/code-switch-escape-hatch-test/) are sooo handy.
Imagine the test fails and I run this in my R session probably after throwing a `browser()`[^debug] somewhere and running
`devtools::load_all()`:
```{r}
temp_dir <- withr::local_tempdir()
withr::local_options(blop = FALSE)
usethis::local_project(temp_dir, force = TRUE)
withr::local_envvar("TEST_SWITCH" = "something")
```
[^debug]: For better debugging advice, refer to [Shannon Pileggi's materials](https://www.pipinghotdata.com/talks/2022-11-11-debugging/)!
Then I do the actual debugging.
At the end my session is all messy!
```{r}
Sys.getenv("TEST_SWITCH")
getOption("blop")
usethis::proj_sitrep() # cool function!
```
Instead of fixing each thing by hand I can run
```{r}
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!
```{r, eval=FALSE}
Sys.getenv("TEST_SWITCH")
getOption("blop")
usethis::proj_get()
```
So I can go and debug the next failing test. :smile_cat:
## 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](https://github.com/r-lib/rlang/blob/7a78dc3f0d5b2fb73289f820e39afb5c4d665802/R/import-standalone-defer.R). 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!