Customize testthat snapshot directory with monkey patching

Nan Xiao January 5, 2025 2 min read
Memories instant photo. Photo by Kelly Sikkema.
Memories instant photo. Photo by Kelly Sikkema.

Problem

Snapshot testing has been supported in testthat since testthat 3.0.0. By default, snapshot files are saved to a hardcoded directory:

tests/testthat/_snaps/

Unfortunately, testthat does not provide a public API to customize this directory. This question has been raised in issues like r-lib/testthat#1623, suggesting that customizing the snapshot directory is a valid use case.

Solution

Without a public API, the most practical solution is to modify testthat’s internal behavior at runtime through monkey patching. This approach avoids creating a custom version of testthat while allowing the flexibility we need.

In R, monkey patching can be easily implemented using namespace utility functions. For example, David Lukes described this approach for customizing the range filter behavior in the DT package.

The following code demonstrates how to change the testthat snapshot directory by patching relevant internal functions. Add it to tests/testthat/helper.R or another testthat helper file:

#' Modify a testthat function to use a different snapshot directory at runtime
#'
#' @param f Function name to modify.
#' @param new New snapshot directory path.
#' @param old Old snapshot directory path. Defaults to `"_snaps"`.
set_snapshot_dir <- function(f, new, old = "_snaps") {
  func <- getFromNamespace(f, ns = "testthat")
  code <- deparse(body(func))
  code <- gsub(sprintf('"%s"', old), sprintf('"%s"', new), code, fixed = TRUE)
  body(func) <- parse(text = paste(code, collapse = "\n"))
  assignInNamespace(f, func, ns = "testthat")
}

set_snapshot_dir("test_files_reporter", "_snapshots")
set_snapshot_dir("snapshot_meta", "_snapshots")

With this code, snapshots will be saved to tests/testthat/_snapshots/ instead of tests/testthat/_snaps/. This approach has been tested with testthat 3.2.2 (the latest release at the time of writing) and retains the original snapshot testing behavior:

  • You can use expect_snapshot() and expect_snapshot_file() as usual.
  • The tests run successfully with devtools::test() and R CMD check without additional notes, warnings, or errors.
  • Repeated test runs do not cause issues, even with the patched functions.

Relative paths are also supported for custom paths. For example, to save snapshots in a directory parallel to your package/project directory:

set_snapshot_dir("test_files_reporter", "../../../testdata/_snapshots")
set_snapshot_dir("snapshot_meta", "../../../testdata/_snapshots")

Caveats

Dependency on testthat version

Monkey patching relies on the internal structure of testthat, which may change in future versions. To avoid compatibility issues, consider using a dependency management tool like renv or shared baselines to lock down specific package versions in your project.

Avoid absolute paths

Avoid using absolute paths for custom snapshot directories. They will reduce the portability of your tests across different environments and platforms. Relative paths are more robust, as they depend only on the structure of your project(s) rather than the file system setup.