Skip to content

Commit f6a0bc0

Browse files
authored
Include discussion of embedded skips (#1764)
Fixes #1639
1 parent d369373 commit f6a0bc0

File tree

3 files changed

+51
-38
lines changed

3 files changed

+51
-38
lines changed

R/test-env.R

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#' * `testing_package()` gives name of the package being tested.
77
#'
88
#' These are thin wrappers that retrieve the values of environment variables.
9-
#' To avoid creating a run-time dependency on testthat, you can inline the
10-
#' source of these functions directly into your package.
9+
#' You will usually want to avoid a runtime dependency on testthat, i.e. you
10+
#' won't want to use testthat in the code below `R/`. In that case, you should
11+
#' inline the source of these functions into your package, in whatever location
12+
#' you are using for any other test helper functions.
1113
#'
1214
#' @export
1315
is_testing <- function() {

man/is_testing.Rd

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vignettes/skipping.Rmd

+43-34
Original file line numberDiff line numberDiff line change
@@ -51,57 +51,66 @@ All reporters show which tests as skipped.
5151
As of testthat 3.0.0, ProgressReporter (used interactively) and CheckReporter (used inside of `R CMD check`) also display a summary of skips across all tests.
5252
It looks something like this:
5353

54-
── Skipped tests ───────────────────────────────────────────────────────
55-
● No token (3)
56-
● On CRAN (1)
54+
```
55+
── Skipped tests ───────────────────────────────────────────────────────
56+
● No token (3)
57+
● On CRAN (1)
58+
```
5759

5860
You should keep an on eye this when developing interactively to make sure that you're not accidentally skipping the wrong things.
5961

6062
## Helpers
6163

6264
If you find yourself using the same `skip_if()`/`skip_if_not()` expression across multiple tests, it's a good idea to create a helper function.
63-
This function should start with `skip_` and live somewhere in your `R/` directory.
65+
This function should start with `skip_` and live in a `test/helper-{something}.R` file:
6466

6567
```{r}
66-
skip_if_Tuesday <- function() {
67-
if (as.POSIXlt(Sys.Date())$wday != 2) {
68-
return(invisible(TRUE))
68+
skip_if_dangerous <- function() {
69+
if (!identical(Sys.getenv("DANGER"), "")) {
70+
skip("Not run in dangerous environments.")
71+
} else {
72+
invisible()
6973
}
70-
71-
skip("Not run on Tuesday")
7274
}
7375
```
7476

75-
It's important to test your skip helpers because it's easy to miss if you're skipping more often than desired, and the test code is never run.
76-
This is unlikely to happen locally (since you'll see the skipped tests in the summary), but is quite possible in continuous integration.
77-
78-
For that reason, it's a good idea to add a test that you skip is activated when you expect.
79-
skips are a special type of condition, so you can test for their presence/absence with `expect_condition()`.
80-
For example, imagine that you've defined a custom skipper that skips tests whenever an environment variable `DANGER` is set:
81-
82-
```{r}
83-
skip_if_dangerous <- function() {
84-
if (identical(Sys.getenv("DANGER"), "")) {
85-
return(invisible(TRUE))
77+
## Embedding `skip()` in package functions
78+
79+
Another useful technique that can sometimes be useful is to build a `skip()` directly into a package function.
80+
For example take a look at [`pkgdown:::convert_markdown_to_html()`](https://github.com/r-lib/pkgdown/blob/v2.0.7/R/markdown.R#L95-L106), which absolutely, positively cannot work if the Pandoc tool is unavailable:
81+
82+
```{r eval = FALSE}
83+
convert_markdown_to_html <- function(in_path, out_path, ...) {
84+
if (rmarkdown::pandoc_available("2.0")) {
85+
from <- "markdown+gfm_auto_identifiers-citations+emoji+autolink_bare_uris"
86+
} else if (rmarkdown::pandoc_available("1.12.3")) {
87+
from <- "markdown_github-hard_line_breaks+tex_math_dollars+tex_math_single_backslash+header_attributes"
88+
} else {
89+
if (is_testing()) {
90+
testthat::skip("Pandoc not available")
91+
} else {
92+
abort("Pandoc not available")
93+
}
8694
}
8795
88-
skip("Not run in dangerous enviromnents")
96+
...
8997
}
9098
```
9199

92-
Then you can use `expect_condition()` to test that it skips tests when it should, and doesn't skip when it shouldn't:
100+
If Pandoc is not available when `convert_markdown_to_html()` executes, it throws an error *unless* it appears to be part of a test run, in which case the test is skipped.
101+
This is an alternative to implementing a custom skipper, e.g. `skip_if_no_pandoc()`, and inserting it into many of pkgdown's tests.
93102

94-
```{r}
95-
test_that("skip_if_dangerous work", {
96-
# Test that a skip happens
97-
withr::local_envvar(DANGER = "yes")
98-
expect_condition(skip_if_dangerous(), class = "skip")
99-
100-
# Test that a skip doesn't happen
101-
withr::local_envvar(DANGER = "")
102-
expect_condition(skip_if_dangerous(), NA, class = "skip")
103-
})
103+
We don't want pkgdown to have a runtime dependency on testthat, so pkgdown includes a copy of `testthat::is_testing()`:
104+
105+
```{r eval = FALSE}
106+
is_testing <- function() {
107+
identical(Sys.getenv("TESTTHAT"), "true")
108+
}
104109
```
105110

106-
Testing `skip_if_Tuesday()` is harder because there's no way to control the skipping from the outside.
107-
That means you'd need to "mock" its behaviour in a test, using the [mockery](https://github.com/r-lib/mockery) or [mockr](https://krlmlr.github.io/mockr/) packages.
111+
It might look like the code appears to still have a runtime dependency on testthat, because of the call to `testthat::skip()`.
112+
But `testthat::skip()` is only executed during a test run, which implies that testthat is installed.
113+
114+
We have mixed feelings about this approach.
115+
On the one hand, it feels elegant and concise, and it absolutely guarantees that you'll never miss a needed skip in one of your tests.
116+
On the other hand, it mixes code and tests in an unusual way, and when you're focused on the tests, it's easy to miss the fact that a package function contains a `skip()`.

0 commit comments

Comments
 (0)