我遇到了同样的问题,我的测试在 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"
file_two 的内容:
[default]
value_X = "fuzzly"
value_Z = "puzzly"
[profile peter]
value_X = "fuzzly"
value_Z = "puzzly"
如您所见,当涉及到命名配置文件时,文件 2 中的命名是否遵循另一个命名约定。配置文件写在“[]”中,两个文件中的默认配置文件始终是“[默认]”。但是一旦涉及到命名配置文件,它就只是在一个文件中使用“[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() 也没有发现任何问题。
虽然整个代码都在演变,但我认为我应该事先阅读文件内容,并只将已读取的 read_in 内容提供给函数,以避免代码重复。所以我改变了这样的功能:
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,"=."))))
}
您可能会注意到,我只更改了函数主体的第一行,而 if 条件保持不变 - 我的错误!
但是我的测试没有抛出错误,因为 if 条件仍然有效。即使 'file_content == "file_two"' 部分现在生成了一个逻辑向量,并且 if() ... else ... 通常会在逻辑长度 > 1 时引发警告。带有 && 的特殊构造不会抛出这样的错误,因为它返回一个 length(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() 失败时,您的代码中可能存在一些更深层次的错误和缺陷,您必须注意!