R 包开发:测试在控制台中通过,但通过 devtools::test() 失败

R package development: tests pass in console, but fail via devtools::test()

我正在开发一个 R 包,它调用包 rstan 中的函数。作为 MWE,我的测试文件目前是这样设置的,使用从 rstan 的示例中逐字获取的代码:

library(testthat)
library(rstan)

# stan's own example
stancode <- 'data {real y_mean;} parameters {real y;} model {y ~ normal(y_mean,1);}'
mod <- stan_model(model_code = stancode, verbose = TRUE)
fit <- sampling(mod, data = list(y_mean = 0))
# I added this line, and it's the culprit
summary(fit)$summary

当我在控制台或通过 RStudio 中的“运行 测试”按钮 运行 这段代码时,不会抛出任何错误。但是,当我 运行 devtools::test() 时,我得到:

Error (test_moments.R:11:1): (code run outside of `test_that()`)
Error in `summary(fit)$summary`: $ operator is invalid for atomic vectors

而且这个错误绝对不会发生在最后一行代码的上游,因为删除最后一行允许 devtools::test() 到 运行 而不会出错。我正在 运行 更新软件包 devtoolsrstan

似乎 devtools::test 在 S4 调度不以通常方式工作的设置中评估测试代码,至少对于您在测试文件中显式加载的包(在这种情况下 rstan).结果,summary 分派到 summary.default 而不是 rstan 中为 class "stanfit".

实现的 S4 方法

您看到的行为可能与 testthat 存储库中的 this issue 有关,这似乎尚未解决。

这里是一个最小的例子,它试图阐明正在发生的事情,展示了一种可能的(公认的不方便的)解决方法。

pkgname <- "foo"
usethis::create_package(pkgname, rstudio = FALSE, open = FALSE)
setwd(pkgname)

usethis::use_testthat()
path_to_test <- file.path("tests", "testthat", "test-summary.R")
text <- "test_that('summary', {
  library('rstan')
  stancode <- 'data {real y_mean;} parameters {real y;} model {y ~ normal(y_mean,1);}'
  mod <- stan_model(model_code = stancode, verbose = TRUE)
  fit <- sampling(mod, data = list(y_mean = 0))

  expect_identical(class(fit), structure('stanfit', package = 'rstan'))
  expect_true(existsMethod('summary', 'stanfit'))

  x <- summary(fit)
  expect_error(x$summary)
  expect_identical(x, summary.default(fit))
  print(x)

  f <- selectMethod('summary', 'stanfit')
  y <- f(fit)
  str(y)
})
"
cat(text, file = path_to_test)

devtools::test(".") # all tests pass

如果您的包实际上导入了 rstan(在 NAMESPACE 意义上,而不是在 DESCRIPTION 意义上),那么 S4 调度似乎工作正常,大概是因为 devtools 在 运行 任何测试之前以“正确”的方式加载你的包及其依赖项。

cat("import(rstan)\n", file = "NAMESPACE")
newtext <- "test_that('summary', {
  stancode <- 'data {real y_mean;} parameters {real y;} model {y ~ normal(y_mean,1);}'
  mod <- stan_model(model_code = stancode, verbose = TRUE)
  fit <- sampling(mod, data = list(y_mean = 0))

  x <- summary(fit)
  f <- selectMethod('summary', 'stanfit')
  y <- f(fit)
  expect_identical(x, y)
})
"
cat(newtext, file = path_to_test)

## You must restart your R session here. The current session 
## is contaminated by the previous call to 'devtools::test',
## which loads packages without cleaning up after itself...

devtools::test(".") # all tests pass

如果您的测试失败 并且 您的包导入 rstan,那么可能会发生其他事情,但如果没有您的最小版本,很难诊断包。

免责声明:不遗余力地导入 rstan 以解决一个相对晦涩的 devtools 问题应该被认为更多的是破解而不是修复,并相应地记录...