如何为建议的包编写单元测试?

How to write unit tests for suggested packages?

R 中的包可以对其他包有不同类型的依赖关系。其中一些类型表示硬性要求,即 DependsImportsLinkingTo.

然而,还有第二类表示更软的依赖性,即 SuggestsEnhances。在这两种情况下,如果建议/增强包可用,则包会提供额外的功能。

这是一个具体的例子:包 checkpoint 导入 knitr 因为 knitr 帮助 checkpoint 解析 rmarkdown 个文件。

但现在我正在考虑将 knitr 更改为 Suggests 依赖项,即仅在实际安装 knitr 时才提供此功能。

为了进行正确的单元测试,这意味着我必须测试两种情况:

  1. 如果 knitr 可用,那就去做吧。
  2. 如果 knitr 不可用,则抛出警告,什么也不做。

实际的R代码很简单:

if(require(knitr)) {
  do_stuff()
} else {
  message("blah")
}

问题

但是如何为这两种情况设置单元测试?

在我看来,检查 require(knitr) 的简单事实将加载 knitr 包(如果它在本地库中可用)。

因此,要测试案例 1,我必须在本地安装 knitr,这意味着我无法测试案例 2。

有没有办法为这个用例配置 testthat(或任何其他单元测试框架)?

tl;博士

测试使用require(knitr)失败时后面的b运行ch,使用trace()临时修改require(),这样就找不到knitr,即使它出现在 .libPaths() 上。具体来说,在 require() 的正文中,将 lib.loc= 的值重置为指向 R.home() -- 一个不包含 knitr 包的现有目录。

这似乎在包中和在交互式会话中一样有效,在交互式会话中您 运行 以下内容:

find.package("knitr")

trace("require", quote(lib.loc <- R.home()), at=1)
isTRUE(suppressMessages(suppressWarnings(require(knitr))))

untrace("require")
isTRUE(suppressMessages(suppressWarnings(require(knitr))))

据我了解,您有一个包含两个 b运行ches 的函数,一个在 require(knitr) 成功的 R 会话中执行,另一个在它成功的会话中执行失败。然后您想要从单个 R 实例测试此函数 "both ways",其中 knitr 实际上位于 .libPaths().

所以基本上你需要一些方法来暂时屏蔽 require(knitr) 的调用,使 knitr 的实际存在不可见。完全临时重置 .libPaths() 返回的值看起来很有希望,但似乎不可能。

另一个有前途的途径是在从 NULL 调用 require() 时以某种方式重置 lib.loc 的默认值(这意味着“使用 .libPaths() 的值)到knitr 不可用的其他一些位置。您不能通过覆盖 base::require() 来完成此操作,也不能(在包中)通过定义本地掩码版本到达那里require() 与期望值 lib.loc

不过,看起来您可以使用 trace() 临时修改 require()(将其隐藏到 knitr 的可用性设置 lib.loc=R.home())。然后执行 untrace()require() 恢复为原版,然后继续查找 knitr

这是我测试过的虚拟包中的样子。首先是一个 R 函数,它允许我们沿着两个 b运行ches

测试是否成功
## $PKG_SRC/R/hello.R

hello <- function(x=1) {
    if(require(knitr)) {
        x==2
    } else {
        x==3
    }
}

然后进行一些测试,每个 b运行ch:

## $PKG_SRC/inst/tests/testme.R

## Test the second branch, run when require(knitr) fails
trace("require", quote(lib.loc <- R.home()), at=1)
stopifnot(hello(3))
untrace("require")

## Test the first branch, run when require(knitr) succeeds
stopifnot(hello(2))

为了测试这个,我用pkgKitten::kitten("dummy")建立了一个源码目录,将这两个文件复制进去,在DESCRIPTION文件中添加Suggests: knitr,然后运行 devtools::install()devtools::check() 从适当的目录。该软件包安装得很好,并通过了所有检查。