在使用devtools::check时,testthat测试失败但在devtools::test中却可以正常工作。

18

有没有办法重现devtools :: check使用的环境?

我的问题是,我的测试可以使用devtools :: test(),但在devtools :: check()中失败。 我现在的问题是如何找到问题所在。 check的报告只打印错误日志的最后几行,我找不到完整的测试报告。

checking tests ... ERROR
Running the tests in ‘tests/testthat.R’ failed.
Last 13 lines of output:
...

我知道check使用的环境与test不同,但我不知道应该如何调试这些问题,因为它们根本无法重现。特别是这些测试在几个月前运行,所以不确定问题出在哪里。

编辑

实际上,我尝试定位我的问题,并找到了解决方案。但是为了将我的解决方案发布出来,我必须添加更多细节。

因此,我的测试总是失败,因为我正在测试一个Markdown脚本是否可以无错误运行,之后我会检查是否正确设置了一些环境变量。这些是我使用脚本计算的结果以及我设置的标准设置。因此,如果我在开发后忘记更改设置,则希望收到警告...

无论如何,由于这是一个Markdown脚本,我必须提取代码,并且我正在使用此帖子中的注释knitr: run all chunks in an Rmarkdown document使用knitr::purl获取代码和sys.source进行执行。

runAllChunks <- function(rmd, envir=globalenv()){
  # as found here https://dev59.com/g2Af5IYBdhLWcg3wVxjh
  tempR <- tempfile(tmpdir = '.', fileext = ".R")
  on.exit(unlink(tempR))
  knitr::purl(rmd, output=tempR, quiet=TRUE)
  sys.source(tempR, envir=envir)
}

由于某些原因,自从大概几周以前(不确定是哪些最近安装的新软件包...),这会产生错误。但由于有一条新评论,我可以使用 knitr::knit 来执行代码,这样就像预期的那样工作了,现在我的测试不再抱怨。

所以最终,我不知道问题到底出在哪里,但现在它可以工作了。

7个回答

4

我最近遇到了一个类似的问题,我的测试在使用devtools::test()运行时是成功的,但在使用devtools::check()运行时失败了。我不知道这个解决方案是否能够修复上述问题,但它应该有助于查找类似的问题。

在我的情况下,问题最终归结为使用需要在Suggests中列出的包而不是Imports/Depends中列出的包的函数。特别是,我的函数调用了httr::content(),当我尝试传递as = "parsed"参数时就会出错。原来as = "parsed"使用了一个建议使用的包readr来读取csv文件,我需要将它添加到我的依赖项中才能让devtools::check()正常工作。


你能详细说明一下吗?这难道不值得在“devtools”上进行公关吗?我遇到了类似的问题,而且这不是一个容易解决的问题。 - Konrad
1
我当时没有想到这一点(我只是想在这里记录我的经验),但可能值得至少开一个问题来改进错误信息的提示。为了澄清:我看到的是,如果你依赖于包A,但使用A中直接使用包B的函数,则会出现问题。如果A建议使用B,则它将通过检查。但是,如果B未在您的依赖项中声明,则test()将工作,因为它是交互式的,但check()将失败,因为它从完全干净的状态开始。 - Mirabilis

2
这是一个关于testthat的已知问题。解决方法是在tests/testthat.R文件的第一行添加以下内容:
Sys.setenv(R_TESTS="")

4
实际上,这并没有解决问题。与之前相同的问题...并且仍然没有可能读取testthat执行日志 :-( - drmariod

1

如果对其他人有帮助,这是对我有效的方法:

  1. 重新安装所有相关软件包。例如:install.packages("testthat", "dplyr", "lubridate", "stringr")(我包括了我的软件包使用的所有软件包)。
  2. 关闭RStudio并重新打开

然后所有测试都通过了。


1
我花了太多时间研究这个错误,希望以后能帮到别人。我想补充一下,在我的函数中使用ggplot2 :: autoplot()时出现了此错误,并且需要在我的函数的Roxygen骨架部分中添加@import ggfortify

1

我在使用devtools::check()进行测试时遇到了与testthat::test()不同的测试失败问题。

但以上解决方法都不适用于我的问题,因此我决定在这里发布我的问题和解决方案。但首先,根据我的经验,需要注意以下几点:

devtools::check()似乎进行了更深层次的错误检查,超过了你自己编写的测试。

现在来看我的代码设置。我有一个函数,用于从两个不同的文件中检索值。这些文件包含了具有一组值的命名配置文件。但是,这些配置文件的名称因文件而异:

示例文件:

file_one的内容:

[default]
value_A = "foo"
value_B = "bar"
value_C = "baz"

[peter]
value_A = "oof"
value_B = "rab"
value_C = "zab"

文件二的内容:

[default]
value_X = "fuzzly"
value_Z = "puzzly"

[profile peter]
value_X = "fuzzly"
value_Z = "puzzly"

如您所见,第二个文件中的命名是否遵循另一种命名约定,特别是对于已命名配置文件。这些配置文件都是用“[]”编写的,而默认配置文件在两个文件中始终为“[default]”。但是,一旦涉及到命名配置文件,一个文件中只是“[name]”,而另一个文件中则是“[profile name]”。

现在我已经构建了这样的函数(简化版):

get_value <- function(file_content, what, profile) {
  file_content <- readr::read_lines(file)
  all_profiles_at <- grep("\\[.*\\]", file_content)
  profile_regex <- paste0("\\[",if(file_content == "file_two" && profile != "default") "profile ",profile,"\\]")
  profile_at <- grep(profile_regex, file_content)
  profile_ends_at <- if(profile_at == max(all_profiles_at)) length(file_content) else all_profiles_at[grep(paste0("^",profile_at,"$"), all_profiles_at) + 1] -1
  profile_content <- file_content[profile_at:profile_ends_at]
  whole_what <- stringr::str_replace_all(profile_content[grep(paste0("^",what,".*"), profile_content)], " ", "")
  return(stringr::str_sub(whole_what, stringr::str_length(paste0(what,"=."))))
}

使用这段代码后,我的测试运行得非常顺利,即使check()函数也没有发现任何问题。
随着整个代码的演变,我意识到应该提前读取文件内容,并将已经读入的内容传递给函数,以避免代码重复。因此,我对函数进行了如下更改:
get_value <- function(file, what, profile) {
  is_file_two <- is_file_two(file_content)
  all_profiles_at <- grep("\\[.*\\]", file_content)
  profile_regex <- paste0("\\[",if(file_content == "file_two" && profile != "default") "profile ",profile,"\\]")
  profile_at <- grep(profile_regex, file_content)
  profile_ends_at <- if(profile_at == max(all_profiles_at)) length(file_content) else all_profiles_at[grep(paste0("^",profile_at,"$"), all_profiles_at) + 1] -1
  profile_content <- file_content[profile_at:profile_ends_at]
  whole_what <- stringr::str_replace_all(profile_content[grep(paste0("^",what,".*"), profile_content)], " ", "")
  return(stringr::str_sub(whole_what, stringr::str_length(paste0(what,"=."))))
}   

作为你的助手,我来翻译以下内容,它涉及到IT技术。请注意,我只更改了函数体的第一行,而没有更改if条件 - 这是我的错误!但我的测试并没有报错,因为if条件仍然有效。即使 'file_content == "file_two" ' 部分现在生成逻辑向量,并且 if() … else … 通常会在逻辑长度>1时发出警告。特殊结构与&&不会引发此类错误,因为它返回长度(1)的逻辑值:
# with warning
if(c(FALSE, FALSE, FALSE)) "Done!" else "Not done!"
# no warning:
if(c(FALSE, FALSE, FALSE) && TRUE) "Done!" else "Not done!"

这就是为什么我使用testthat::test()进行测试仍然有效的原因。
但是devtools::check()发现了我的代码中的缺陷,导致测试失败!
FAILURE_REPORT 的这部分显示了我的错误:
[...]
    where 41: test_check("my_package_name")
    
     --- value of length: 18 type: logical ---
     [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
    [13] FALSE FALSE FALSE FALSE FALSE FALSE
     --- function from context ---
[...]

结论: testthat::test()非常好用!它可以检查你的代码是否仍然能够运行。但是,devtools::check()更深入 - 当你用testthat::test()测试通过,但是用devtools::check()测试失败时,你可能有一些更深层次的错误和缺陷,这些问题必须得到解决!

0
这种情况也可能发生在以下场景中:您已经在 R 中加载了一个库,并且在没有命名空间绑定的情况下引用该库中的函数。例如,假设您在测试文件中使用来自Matrixnnzero() 函数并且恰好已经使用library(Matrix)加载了Matrix包。 然后devtools:: test()将通过,但devtools:: check()失败。 使用Matrix::nnzero()应该可以解决这个问题。

0

正如我上面简要提到的,我改变了一些代码,不再使用knitr::purl,而是使用knitr::knit,这解决了我的问题。

expect_error(f <- runAllChunks('010_main_lfq_analysis.Rmd'), NA)
expect_error(f <- knitr::knit('010_main_lfq_analysis.Rmd', output='jnk.R', quiet=TRUE, envir=globalenv()), NA)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接