我在使用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")
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE FALSE
[...]
结论:
testthat::test()非常好用!它可以检查你的代码是否仍然能够运行。但是,devtools::check()更深入 - 当你用testthat::test()测试通过,但是用devtools::check()测试失败时,你可能有一些更深层次的错误和缺陷,这些问题必须得到解决!
test()
将工作,因为它是交互式的,但check()
将失败,因为它从完全干净的状态开始。 - Mirabilis