如何测试依赖于正在安装或未安装的包的行为
how to test behavior that depends on a package being installed or not
我有这样的功能:
func <- function(x) {
if (requireNamespace("broom", quietly = TRUE)) {
print(1)
} else {
print(2)
}
我想使用 testthat
编写测试来触发 两种 情况。但当然 broom
要么安装在我的电脑上,要么没有安装。怎么办?
编辑:自 testthat-2.0.0
起不再有效。根据 change in Oct 2017:
其余答案仅适用于 testthat
的旧版本。
testthat::with_mock
应该做你想做的。
library(testthat)
somefunc <- function() if (requireNamespace("base", quietly=TRUE)) 1L else 2L
一些简单的测试:
expect_equal( somefunc(), 1L )
成功。
expect_equal( somefunc(), 2L )
# Error: somefunc() not equal to 2.
# 1/1 mismatches
# [1] 1 - 2 == -1
预期。
让我们创建一个覆盖基本函数的 "mock" 函数:
with_mock(
`base::requireNamespace` = function(package, ..., quietly=FALSE) FALSE,
expect_equal( somefunc(), 1L )
)
# Error: somefunc() not equal to 1.
# 1/1 mismatches
# [1] 2 - 1 == 1
with_mock(
`base::requireNamespace` = function(package, ..., quietly=FALSE) FALSE,
expect_equal( somefunc(), 2L )
)
# [1] 2
注意:成功时,expect_equal
不可见 return 是 return 值,因此您在第一个示例中看不到 [1] 1
。 with_mock
,成功时,return 是 return 值,但不是不可见的。在这两种情况下,失败都会 return 修改后的 return 值。这个微小的差异应该不会影响任何测试。
根据您要覆盖的函数,(对我而言)小心使用相同的形式定义模拟函数是有意义的。如果你知道 exactly 它是如何在 all 从属函数中调用的 always在你的测试期间,但我认为对形式的额外仔细关注将排除真正难以解决的测试失败。
注意:帮助说明这个
... is still experimental, so use with care.
old_fn <- base::requireNamespace
myrequireNamespace <- function(...) FALSE
unlockBinding("requireNamespace", as.environment("package:base"))
assign("requireNamespace",myrequireNamespace, "package:base")
expect_error(read_data("meta",data_table, frame=NULL),"Package \"readxl\"
needed for this function to load excel files")
assign("requireNamespace",old_fn, "package:base")
lockBinding("requireNamespace", as.environment("package:base"))
rm(old_fn, myrequireNamespace)
对我来说是一个在 testthat 内部工作的解决方法。
我有这样的功能:
func <- function(x) {
if (requireNamespace("broom", quietly = TRUE)) {
print(1)
} else {
print(2)
}
我想使用 testthat
编写测试来触发 两种 情况。但当然 broom
要么安装在我的电脑上,要么没有安装。怎么办?
编辑:自 testthat-2.0.0
起不再有效。根据 change in Oct 2017:
其余答案仅适用于 testthat
的旧版本。
testthat::with_mock
应该做你想做的。
library(testthat)
somefunc <- function() if (requireNamespace("base", quietly=TRUE)) 1L else 2L
一些简单的测试:
expect_equal( somefunc(), 1L )
成功。
expect_equal( somefunc(), 2L )
# Error: somefunc() not equal to 2.
# 1/1 mismatches
# [1] 1 - 2 == -1
预期。
让我们创建一个覆盖基本函数的 "mock" 函数:
with_mock(
`base::requireNamespace` = function(package, ..., quietly=FALSE) FALSE,
expect_equal( somefunc(), 1L )
)
# Error: somefunc() not equal to 1.
# 1/1 mismatches
# [1] 2 - 1 == 1
with_mock(
`base::requireNamespace` = function(package, ..., quietly=FALSE) FALSE,
expect_equal( somefunc(), 2L )
)
# [1] 2
注意:成功时,expect_equal
不可见 return 是 return 值,因此您在第一个示例中看不到 [1] 1
。 with_mock
,成功时,return 是 return 值,但不是不可见的。在这两种情况下,失败都会 return 修改后的 return 值。这个微小的差异应该不会影响任何测试。
根据您要覆盖的函数,(对我而言)小心使用相同的形式定义模拟函数是有意义的。如果你知道 exactly 它是如何在 all 从属函数中调用的 always在你的测试期间,但我认为对形式的额外仔细关注将排除真正难以解决的测试失败。
注意:帮助说明这个
... is still experimental, so use with care.
old_fn <- base::requireNamespace
myrequireNamespace <- function(...) FALSE
unlockBinding("requireNamespace", as.environment("package:base"))
assign("requireNamespace",myrequireNamespace, "package:base")
expect_error(read_data("meta",data_table, frame=NULL),"Package \"readxl\"
needed for this function to load excel files")
assign("requireNamespace",old_fn, "package:base")
lockBinding("requireNamespace", as.environment("package:base"))
rm(old_fn, myrequireNamespace)
对我来说是一个在 testthat 内部工作的解决方法。