能否在testthat测试或run_examples()中使用R包数据?

34

我正在开发一个R包,使用devtools、testthat和roxygen2。在data文件夹中有几个数据集(foo.txt和bar.csv)。

我的文件结构如下:

/ mypackage
    / data
        * foo.txt, bar.csv
    / inst
        / tests
            * run-all.R, test_1.R
    / man
    / R

我相当确定'foo'和'bar'的文档已经正确记录:

    #' Foo data
    #'
    #' Sample foo data
    #'
    #' @name foo
    #' @docType data
    NULL
    #' Bar data
    #'
    #' Sample bar data
    #'
    #' @name bar
    #' @docType data
    NULL

我希望在我的文档示例和单元测试中使用“foo”和“bar”中的数据。

例如,我想通过调用以下命令在我的testthat测试中使用这些数据集:

    data(foo)
    data(bar)
    expect_that(foo$col[1], equals(bar$col[1]))

同时,我希望文档中的示例代码呈现如下格式:

    #' @examples
    #' data(foo)
    #' functionThatUsesFoo(foo)

如果在开发软件包时尝试调用data(foo),将会收到“找不到数据集 'foo'”的错误。但是,如果构建软件包、安装并加载后,就可以使测试和示例工作。

我目前的解决方法是不运行示例:

    #' @examples
    #' \dontrun{data(foo)}
    #' \dontrun{functionThatUsesFoo(foo)}

在测试中,使用特定于我的本地计算机的路径预加载数据:

    foo <- read.delim(pathToFoo, sep="\t", fill = TRUE, comment.char="#")
    bar <- read.delim(pathToBar, sep=";", fill = TRUE, comment.char="#"
    expect_that(foo$col[1], equals(bar$col[1]))

这似乎并不理想 - 特别是因为我正在与他人合作 - 要求所有合作者都具有对“foo”和“bar”的相同完整路径。此外,文档中的示例看起来无法运行,即使安装了软件包也是如此。 有什么建议吗?非常感谢。

1
不要使用data()。只依赖于延迟加载。 - hadley
抱歉上次的评论有误,我还在适应这种格式。感谢@hadley。这对testthat测试有所帮助。但我仍然不知道如何在文档示例(使用roxygen2)中利用数据集。 - ldecicco
2
如果您将数据转换为.Rdata文件,则load_all将为您加载它。 - hadley
2个回答

22

在示例/测试中导入非RData文件

我查看了JSONIO包并找到了解决此问题的方法,该包显然需要提供一些读取与.RData不同类型文件的示例。

我在函数级别示例中让它能够正常工作,并且同时满足R CMD check mypackagetestthat::test_package()的要求。

(1) 重新组织您的包结构,使示例数据目录位于inst内。在某个时刻,R CMD check mypackage告诉我将非RData数据文件移动到inst/extdata,因此在这种新结构下,也进行了重命名。

/ mypackage
    / inst
        / tests
            * run-all.R, test_1.R
        / extdata
            * foo.txt, bar.csv
    / man
    / R
    / tests
        * run-testthat-mypackage.R

(2) (可选)添加一个顶层的tests目录,以便您的新testthat测试现在也可以在R CMD check mypackage期间运行。

run-testthat-mypackage.R脚本至少应该有以下两行:

library("testthat")
test_package("mypackage")

请注意,这是使testthat能够在R CMD check mypackage期间调用,否则没有必要。您还应将testthat作为“Suggests:”依赖项添加到您的DESCRIPTION文件中。

(3) 最后,指定您包内路径的秘密配方:

barfile <- system.file("extdata", "bar.csv", package="mypackage")
bar <- read.csv(barfile)
# remainder of example/test code here...

如果您查看system.file()命令的输出,它会返回R框架内您的软件包的完整系统路径。在Mac OS X上,它看起来像这样:

"/Library/Frameworks/R.framework/Versions/2.15/Resources/library/mypackage/extdata/bar.csv"

我认为这种做法没问题的原因是,除了包内部的路径特性之外,您没有硬编码任何其他路径特性,因此这种方法应该相对于其他系统上的其他 R 安装程序具有鲁棒性。

data() 方法

至于 data() 语义,据我所知,这仅适用于顶级 data 目录中的 R 二进制(.RData)文件。因此,您可以通过预先导入数据文件并使用 save() 命令将它们保存到数据目录中来规避上面的示例。但是,这假设您只需要展示一个数据已经加载到 R 中的示例,而不需要还演示导入文件的上游过程。


感谢您详细的回答! - ldecicco
不用谢。我很高兴能帮到你。这对我的软件包开发也变得有用了,所以我想分享一下。 - Paul 'Joey' McMurdie
我一直在尝试弄清如何使“上游导入过程”也具有可重复性。 我通常的使用情况是,我想使用某些shapefile的转换,这并不是琐碎的事情 - 也许需要一分钟左右。 我可以将shapefile包含在inst/extdata中,但我似乎永远无法从在install()内执行的代码中找到该路径。此外,即使是document()也似乎要重新构建data/中的所有.r文件。我不想每次添加或更改函数文档时都重建它们。data/Makefile似乎很笨拙,有什么技巧吗? - dholstius
@holstius 为什么你在 data/ 文件夹里放了 .r 文件?你的数据重建测试应该由你的单元测试代码在 testsinst/tests 中运行。如果调查这个问题没有帮助,我建议你创建一个单独的 SO 问题来解决你的评论,并提供一个可重现的示例。 - Paul 'Joey' McMurdie

2
根据 @hadley 的评论,.RData 转换将非常有效。
至于团队成员在不同环境下的协作问题,一种常见的模式是达成共识,使用单个环境变量,例如 FOO_PROJECT_ROOT,每个团队成员都会在其环境中适当地设置它。从那时起,您可以使用相对路径,包括跨项目。
一个特定于 R 的方法是,达成共识,每个团队成员都将一些数据/函数设置在他们的 .Rprofile 文件中。例如,这就是 devtools 在非标准位置查找软件包的方式。
最后但并非最不重要的是,虽然这不是最佳选择,但实际上您可以将开发人员特定的代码放入您的存储库中。如果 @hadley 这样做了,这并不是什么坏事。例如,查看他如何在自己的环境中 激活 certain behaviors in testthat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接