Skipping tests

Some times you have tests that you don’t want to run in certain circumstances. This vignette describes how to skip tests to avoid execution in undesired environments. Skipping is a relatively advanced topic because in most cases you want all your tests to run everywhere. The most common exceptions are:

library(testthat)

Basics

testthat comes with a variety of helpers for the most common situations:

You can also easily implement your own using either skip_if() or skip_if_not(), which both take an expression that should yield a single TRUE or FALSE.

All reporters show which tests as skipped. 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. It looks something like this:

── Skipped tests  ───────────────────────────────────────────────────────
● No token (3)
● On CRAN (1)

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

Helpers

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. This function should start with skip_ and live in a test/helper-{something}.R file:

skip_if_dangerous <- function() {
  if (!identical(Sys.getenv("DANGER"), "")) {
    skip("Not run in dangerous environments.")
  } else {
    invisible()
  }
}

Embedding skip() in package functions

Another useful technique that can sometimes be useful is to build a skip() directly into a package function. For example take a look at pkgdown:::convert_markdown_to_html(), which absolutely, positively cannot work if the Pandoc tool is unavailable:

convert_markdown_to_html <- function(in_path, out_path, ...) {
  if (rmarkdown::pandoc_available("2.0")) {
    from <- "markdown+gfm_auto_identifiers-citations+emoji+autolink_bare_uris"
  } else if (rmarkdown::pandoc_available("1.12.3")) {
    from <- "markdown_github-hard_line_breaks+tex_math_dollars+tex_math_single_backslash+header_attributes"
  } else {
    if (is_testing()) {
      testthat::skip("Pandoc not available")
    } else {
      abort("Pandoc not available")
    }
  }
  
  ...
}

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. This is an alternative to implementing a custom skipper, e.g. skip_if_no_pandoc(), and inserting it into many of pkgdown’s tests.

We don’t want pkgdown to have a runtime dependency on testthat, so pkgdown includes a copy of testthat::is_testing():

is_testing <- function() {
  identical(Sys.getenv("TESTTHAT"), "true")
}

It might look like the code appears to still have a runtime dependency on testthat, because of the call to testthat::skip(). But testthat::skip() is only executed during a test run, which implies that testthat is installed.

We have mixed feelings about this approach. 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. 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().