【问题标题】:how to test behavior that depends on a package being installed or not如何测试依赖于是否安装的包的行为
【发布时间】:2019-10-24 23:16:21
【问题描述】:

我有这样的功能:

func <- function(x) {
  if (requireNamespace("broom", quietly = TRUE)) {

    print(1)

  } else {

    print(2)

  }

我想使用testthat 编写触发两种 情况的测试。但当然broom 要么安装在我的电脑上,要么没有安装。怎么办?

【问题讨论】:

  • 看看 installed.packages()

标签: r testthat


【解决方案1】:

编辑:这不再适用于testthat-2.0.0。根据change in Oct 2017:

  • “无法模拟基础包中的函数”:您不能再使用with_mock() 模拟基础包中的函数,因为由于字节码编译器的更改,这在 R-devel 中不再有效。我建议改用mockery 或mockr。

其余答案仅适用于旧版本的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

预期。

让我们创建一个覆盖基本函数的“模拟”函数:

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 不可见地返回返回值,因此在第一个示例中您看不到[1] 1。 with_mock,成功时返回返回值,但不是不可见的。在这两种情况下,失败都会返回修改后的返回值。这一微小的差异不应影响任何测试。

根据您要覆盖的函数,(对我而言)谨慎定义具有相同形式的模拟函数是有意义的。如果您确切地知道它是如何总是在您的测试期间在所有从属函数中调用的,您也许可以简化此操作,但我认为额外的仔细注意形式将排除真正难以解决的测试失败。

注意:帮助说明这一点

...仍处于试验阶段,因此请谨慎使用。

【讨论】:

  • 我不确定这个答案是否有效...我收到错误“错误:无法在基础包(基础)中模拟函数”
  • 感谢@MichaelChirico,我已经更新了问题以反映它。
  • 谢谢。 FWIW 我在自己的测试中通过requireNamespace = function(...) FALSE; my_test; rm(requireNamespace) 快速完成了它......我想这在很大程度上可以解决问题
  • 只要测试仅在您的代码中完成就可以工作......如果依赖包正在调用它,我怀疑搜索路径会在您的版本之前使用base,所以它可能需要使用其他包(虽然我没有测试过)。再次感谢,让这些答案保持最新是很好的。
  • HTH。你关于 kludge 陷阱的观点是正确的!
【解决方案2】:
    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 内部工作的解决方法。

【讨论】:

    猜你喜欢
    • 2018-11-25
    • 1970-01-01
    • 2013-03-10
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多