rtestthatr-s3

Why do packages loaded inside `test_that()` provide their methods outside of `test_that()` and how can I prevent that?


In my understanding, anything put inside test_that() should be compartmentalized, meaning that if I load a package in test_that(), its functions and methods shouldn't be available in other test_that() calls.

In the example below, there are 3 (empty) tests:

library(testthat)

test_that("foo 1", {
  print("as.matrix.get_predicted" %in% methods(as.matrix))
})
#> [1] FALSE
#> ── Skip (???): foo 1 ───────────────────────────────────────────────────────────
#> Reason: empty test

test_that("foo 2", {
  invisible(insight::get_parameters)
})
#> ── Skip (???): foo 2 ───────────────────────────────────────────────────────────
#> Reason: empty test

test_that("foo 3", {
  print("as.matrix.get_predicted" %in% methods(as.matrix))
})
#> [1] TRUE
#> ── Skip (???): foo 3 ───────────────────────────────────────────────────────────
#> Reason: empty test

Why is that? Are there some workarounds?


Edit: I'm looking for a solution specific to testthat, not another testing framework.


Solution

  • Too long for a comment, but reproducible. The test files are sourced in collation order, each in a new R process, hence the library call in testB.R does not cause the test in testC.R to fail.

    pkgname <- "testpackage"
    testdir <- file.path(pkgname, "tests")
    
    .add <- function(a, b) a + b
    package.skeleton(pkgname, list = ".add")
    dir.create(testdir, recursive = TRUE)
    writeLines("stopifnot(!any(.S3methods(as.data.frame) == \"as.data.frame.lm\"))",
               file.path(testdir, "testA.R"))
    writeLines("library(parameters); stopifnot(any(.S3methods(as.data.frame) == \"as.data.frame.lm\"))",
               file.path(testdir, "testB.R"))
    writeLines("stopifnot(!any(.S3methods(as.data.frame) == \"as.data.frame.lm\"))",
               file.path(testdir, "testC.R"))
    
    tools::Rcmd(c("check", pkgname))
    
    * checking tests ...
      Running ‘testA.R’
      Running ‘testB.R’
      Running ‘testC.R’
     OK
    

    This is the "vanilla" approach to compartmentalizing tests that many people still prefer over testthat and other frameworks, at least partly because it heeds the warning in ?detach:

    The most reliable way to completely detach a package is to restart R.

    and therefore is significantly easier for experts to debug and reason about, but that is perhaps a controversial opinion these days ...