单位测试指南

介绍

单位测试简单易于写入,轻松调用,并在整个软件开发过程中授予大的好处,从早期普遍的探索代码到延迟阶段的长期维护。单位测试通常对那些试一试的人变得不可或缺。在这里,我们解释了如何编写单元测试,如何运行它们,以及它们如何编织到标准的Biocometiond构建过程中。我们希望单位测试将成为软件开发的标准部分,以及生物导体包的组成部分。

我们建议您运行testthat.包从CRAN编写单元测试。RUnit是一个R.的实现敏捷软件开发“XUnit”工具(请参见junit.)其中每个人都试图在各自的语言中鼓励强大的有用软件的快速发展。testthat还借着xunit家族的测试包,以及来自许多创新的红宝石测试库,如rspec睾丸培根黄瓜

[回到顶部]

动机

为什么要进行单元测试呢?

假设您需要一个函数被除以有两个参数,你可以这样定义:

divideBy < - 函数(股息,除法){if(divisor == 0)返回(na)股息/ divisor}

当您开发此功能时,您很可能以各种方式测试它,使用不同的参数,检查结果,直到最终满足它正确执行。但是,除非您采用某种软件测试协议,否则您的测试不太可能成为代码的组成部分。它们可能会分散在不同的文件中,或者它们可能不存在于文件中的重新运行代码,就像ad hoc命令行函数调用时,您有时会记住。

我们建议,一个更好的方法是使用轻巧,正式化单元测试。这只需要很少的惯例和实践:

这是一个跑步测试被除以

test_divideby < -  function(){checkequals(divideby(4,2),2)核检查(is.na(divideby(4,0)))checkequalsnumeric(divideby(4,1.2345),3.24,宽容= 1.0e-4)}

等效测试起诉Testthat:

test_that(“divideby正常工作”,{heippe_equal(divideby(4,2),2)预期(is.na(divideby(4,0)))预期(divideby(4,1.2345),3.24,宽容= 1.0e-4)})

采用这些实践将花费你很少。大多数开发bob电竞体育官网人员发现这些做法简化和缩短了开发时间。另外,他们创造了一个可执行合同-一个简明的和可验证的描述,你的代码应该做什么。有经验的单元测试程序员将创建这样一个测试函数来伴随他们编写的每个函数、方法和类。(但不要让这把你吓跑。甚至在包中添加一个测试也是值得的,原因如下所述。)

bob电竞体育官网开发人员通常在向他们推荐单元测试时经常反叛,计算创建现有代码的单元测试将是一个漫长而繁琐的工作,并且其生产力将受到影响。

但是,单位测试是最好的书面当你开发时代码,而不是在编写包之后。用一些轻量级的正式实践替换您的非正式测试,您将看到您的即时和长期生产力的提高。

考虑每个软件单位(每个功能,方法或类)都设计为执行作业,以返回特定输入的特定输出,或导致某些特定的副作用。单元测试指定这些行为,并提供单个机制 - 驻留在一个或多个文件中的一个或多个测试函数,在标准目录结构中 - 以确保目标函数,方法或类执行其作业。通过该保证,程序员(及其合作者)可以充满信心,继续在更大的程序中使用它。出现错误或添加新功能并添加新功能时,可以为现有的集合添加新测试。您的代码逐渐变得更强大,更强大,但仍然很容易并自动验证。

一些支持者表明单元测试的好处进一步扩展:该代码设计本身提高。他们认为,通过其测试的函数的操作定义鼓励清洁设计,“担忧的分离”,以及边缘案件的合理处理。

最后,单元测试可以采用零碎的。为您的包添加单个测试,即使只是对次要功能的测试,以及您和您的用户都将受益。添加更多测试,因为出现了错误,因为添加了新功能,当您发现自己令您写过的代码之前,您在几个月之前写的。很快,单元测试将是您的标准练习的一部分,您的包裹将具有越来越完整的测试集。

[回到顶部]

决定使用哪个测试框架

runit和testthat都是强大的测试解决方案,这些解决方案都是包装开发的伟大工具,您可以选择为您的包装使用,主要归结为个人偏好。然而,这是每个人的优势和弱点的简短列表。

逃避优势

逃避弱点

Testthat优势

testthat缺点

[回到顶部]

RUnit用法

为您的代码添加测试

需要三件事:

  1. 创建包含函数的文件test_dividesBy对于您要测试的每个功能,使用运行向检查功能。
  2. 在其他目录中添加一些小(和特殊)文件。
  3. 确保运行生物根系套餐可用。

第二步和三个解释在构建过程的约定

这些是运行检查方法:

独舍(表达式,表达式-B)核核心(条件)CheckequalsNumeric(A,B,容差)

在一个典型的测试函数中,可以看到test_divideby.,您可以调用您的程序的函数或方法之一,然后调用适当的运行检查功能以确保结果是正确的。运行报告失败(如果有的话),并提供足够的上下文来跟踪错误。

运行可以测试发生异常(错误)吗

checkException (expr、味精)

但是,测试特定例外通常方便,例如,在该功能中产生警告“异常条件”f < - 函数(){警告(“异常条件”);1}

OBS < -  Trycatch(F(),警告= enditionMessage)检查员(“异常状况”,OBS)

使用错误= ......测试特定错误。

[回到顶部]

构建过程的约定

写入单元测试很容易,尽管必须正确设置Biocucontaint包,以便r cmd检查mypackage查找并运行您的测试。我们采取一些痛苦来描述如何建立,以及幕后发生的事情。(见下一节如果您只想仅测试代码的一小部分时使用的简单技术)。

标准命令r cmd检查mypackage来源并运行您的所有R文件mypackage / tests /目录中。历史上,有时仍然如此,R.包开发人员将他们bob电竞体育官网自己发明和风格的测试代码放入其中的一个或多个文件中测试目录中。

运行被添加到这个已经存在的结构和练习大约2005年,并且添加可以令人困惑,从找到和执行您的测试功能的间接方式开始。(但遵循这些步骤,一切都应该很好。张贴bioc-devel如果你遇到任何困难。)

有两个步骤:

  1. 创建文件MyPackage /测试/ runTests。R.有这些内容:

    生物根系::: testpackage(“mypackage”)
  2. 中创建任意数量的文件mypackage / inst / unittests /用于单元测试函数。您可以将所有测试都放在该目录中的一个文件中,或者分布在多个文件中。所有文件必须遵循这个正则表达式中指定的命名约定:

    pattern =“^ test _。* \\。r $”

    为我们的例子,因此,良好的选择是mypackage / inst / unittests / test_divideby.r或者如果是划分Function是您编写的几个自定义算术函数之一,您为其提供了测试,一个更具有描述性的文件名(我们总是推荐这种做法)可能是mypackage / inst / unittests / test_homebrewarromet.r

[回到顶部]

在开发期间使用测试

r cmd检查mypackage

将运行所有测试。但是在开发类或调试方法或函数时,您可能希望一次只运行一个测试,并且当安装早期版本的包时,您将在其中进行本地探索更改。假设您遵循了上面推荐的目录结构和命名约定,那么您当前的工作目录是Inst,这是您所做的:

/R/divideBy.R'); / /将divideBy.R的值设置为0,然后将divideBy.R的值设置为0

报告了一个失败的测试,如下所示:

错误的checkquals(除以(4,2),3):平均相对差:0.5

[回到顶部]

小结:最小设置

最小的生物体单元测试设置只需要此单线添加到mypackage /描述文件

建议:RUnit BiocGenerics

和两个文件,MyPackage /测试/ runTests。R.

生物根系::: testpackage(“mypackage”)

mypackage / inst / unittests / test_divideby.r

test_divideby < -  function(){checkequals(divideby(4,2),2)核检查(is.na(divideby(4,0)))checkequalsnumeric(divideby(4,1.2345),3.24,宽容= 1.0e-4)}

记住你的unittests / test_xxxx.r.文件或文件,可以有任何名称,只要它们开始测试_

[回到顶部]

Testthat用法

哈德利威克姆,Testthat的主要作者有一个全面的章节测试与testthat在他的R包书里。还有一篇文章testthat:开始使用测试R-Journal。

为包装的TestThat基础架构设置的最简单方法是使用devtools :: fumer_testthat()

然后,您可以自动重新加载您的代码和测试并重新运行它们devtools :: test()

[回到顶部]

从跑步转换为testthat

如果您有一个想要转换为使用测试的现有RUnit项目,那么您需要在包结构中更改以下内容。

  1. devtools :: fumer_testthat()可用于设置Testthat测试结构。
  2. 测试文件存储在测试/ testthat.而不是inst / unittests.并应该从测试。理查德·棉花的runittotesthat包可用于以编程方式转换Runit测试以测试格式。
  3. 你需要添加建议:testthat对你的描述文件而不是建议:RUnit BiocGenerics

[回到顶部]

测试覆盖范围

测试覆盖范围指单元测试测试的包代码的百分比。覆盖率较高的包包含bug的可能性较低。

我们的构建系统计算每个软件包的测试覆盖范围(使用Covr.包),并在包着陆页面上的“测试覆盖”屏蔽中报告结果。这个屏蔽显示的值是一个百分比(从0到100的数字)或“未知”,这可能意味着:

如果测试过长以实现完整的测试覆盖,请参阅长期测试。在实施长期测试之前,我们强烈建议在邮件列表中达到生物核磁体团队,以确保正确使用和理由。

[回到顶部]

额外资源

一些值得阅读的网页资源:

[回到顶部]