Perl的首选单元测试框架是什么?

Mik*_*ike 29 perl unit-testing

我是Perl的新手,我想知道是否有一个首选的单元测试框架?

谷歌向我展示了一些不错的结果,但由于我是新手,我不知道社区内是否有明确的偏好.

Rob*_*t P 37

Perl有一套很棒的测试工具随附!Perl核心有几万个自动检查,并且大多数情况下它们都使用这些标准的Perl框架.他们都使用TAP捆绑在一起 - 测试任何协议.

在Perl中创建TAP测试的标准方法是使用Test :: More系列包,包括Test :: Simple入门.这是一个简单的例子:

use 5.012;
use warnings;

use Test::More tests => 3;

my $foo = 5;
my $bar = 6;

ok $foo == 5, 'Foo was assigned 5.';
ok $bar == 6, 'Bar was assigned 6.';
ok $foo + $bar == 11, 'Addition works correctly.';
Run Code Online (Sandbox Code Playgroud)

输出将是:

ok 1 - Foo was assigned 5.
ok 2 - Bar was assigned 6.
ok 3 - Addition works correctly.
Run Code Online (Sandbox Code Playgroud)

基本上,要开始,您需要做的就是传递一个布尔值和一个解释应该发生什么的字符串!

一旦你超越了这个步骤,Test :: More就会有大量的其他函数让测试更容易(字符串,正则表达式比较,深层结构比较),还有Test :: Harness后端可以测试大型组将各个测试脚本放在一起.

最重要的是,正如Schwern指出的那样,几乎所有的现代Test::模块都在一起工作.这意味着你可以使用Test::Class(正如Markus所指出的)rjh答案中列出的所有伟大的模块.事实上,因为Test :: Builder - Test::More和其他人建立的工具(目前由Schwern维护......感谢Schwern!) - 如果需要,你可以从头开始构建你的OWN测试子程序与所有其他测试框架一起工作.仅此一点就使Perl的TAP系统成为我认为最好的系统之一:一切都在一起工作,每个人都使用相同的工具,你可以通过很少的额外工作添加到框架中以满足您的需求.

  • 你不必.但是,如果您的测试脚本在输出仅1或2个测试的结果后死亡,则Test :: More知道您的测试失败(即使它以错误代码为零退出).您也可以在脚本末尾使用Test :: More'no_plan'`或调用`done_testing()`. (6认同)
  • 您使用的是什么版本的Perl和Test :: More?`perl -v`将为你提供perl版本,你可以从`perl -MTest :: More -e"获得Test :: more测试::更多 - > VERSION()"无论哪种方式,它几乎总是更好如果可以,有一个计划.我发现它起初很乏味,但是当测试意外地在一半时间内死亡,并且没有报告我的几个测试用例时,这是一个救生员.如果不是计划,我就不会知道有问题. (3认同)
  • 在较新的Test :: More(> = 0.88)上,`使用Test :: More;`后跟`done_testing;`最后应该正常工作.否则,你需要做`使用Test :: More qw(no_plan)`. (3认同)
  • 值得注意的是,几乎所有现代`Test ::`模块都可以协同工作.`Test :: More`可以与`Test :: Differences`和`Test :: Deep`结合使用. (3认同)
  • @Mike:这只是另一个可用的故障保险; 这不是必需的.理想情况下,您已经知道运行了多少次测试.事实上,`Test :: More`会告诉你.例如,如果你在脚本中添加另一个测试而不更改顶部的`tests`声明,它会说"#看起来像你计划了3个测试但是运行了4`",这使得改变它变得很简单.我还认为,单元测试的重点在于"硬编码"值.如果您的单元测试每次都运行可变数量的步骤,那么它现在不是很可重复,是吗?:) (2认同)

rjh*_*rjh 13

Perl最受欢迎的测试'框架'是一种称为TAP(Test Anything Protocol)的测试结果格式,它是一组字符串,如下所示:

ok 1 - Imported correctly
ok 2 - foo() takes two arguments
not ok 3 - foo() throws an error if passed no arguments
Run Code Online (Sandbox Code Playgroud)

任何可以生成这些字符串的脚本都算作Perl测试.您可以使用Test :: More为各种条件生成TAP - 检查变量是否等于值,检查模块是否正确导入,或者两个结构(数组/散列)是否相同.但是在真正的Perl精神中,有不止一种方法可以做到,还有其他方法(例如Test :: Class,看起来有点像JUnit!)

测试脚本的一个简单的例子(它们通常结束.t,例如foo.t)

use strict;
use warnings;
use Test::More tests => 3;  # Tell Test::More you intend to do 3 tests

my $foo = 3;
ok(defined $foo, 'foo is defined');
is($foo, 3, 'foo is 3');
$foo++;
is($foo, 4, 'incremented foo');
Run Code Online (Sandbox Code Playgroud)

您可以使用Test :: Harness(通常prove从shell 调用)按顺序运行一系列测试,并获得哪些测试通过或失败的摘要.

测试::更多也可以做一些更复杂的事情,比如标记测试为TODO(不要指望它们通过,但只是以防万一)或SKIP(这些测试被破坏/可选,不运行它们).您可以声明您希望运行的测试数量,因此如果您的测试脚本中途中断,则可以检测到这一点.

一旦开始进行更复杂的测试,您可能会发现其他一些CPAN模块很有用 - 这里有一些例子,但还有很多(很多):

Test :: Exception - 测试您的代码是否抛出错误/不会抛出任何错误
Test :: Warn - 测试您的代码执行/不生成警告
Test :: Deep - 深度比较对象.它们不必相同 - 您可以忽略数组排序,使用正则表达式,忽略对象类等
.Test :: Pod - 确保您的脚本具有POD(文档),并且它是有效的
Test :: Pod ::覆盖范围 - 确保您的POD记录模块中的所有方法/函数
Test :: DBUnit - 测试数据库交互
Test :: MockObject - 制作假装对象来控制测试环境


zed*_*doo 5

绝对从这个页面开始:http://perldoc.perl.org/Test/Simple.html并遵循Test :: Tutorial的引用.


Mar*_*kus 5

如果您练习TDD,您会注意到您的单元测试集正在改变很多.Test :: Class遵循xUnit模式(http://en.wikipedia.org/wiki/XUnit).

对我来说,xUnit的主要好处是在方法中封装每个测试.框架通过测试方法的名称命名每个断言,并添加在每次测试之前和之后运行setup-and teardown方法的可能性.

我也尝试了"perl-ish"方式进行单元测试(仅使用Test :: More),但我发现它有点老式和繁琐.


Kra*_*lew 5

一些反建议可能是这样的:

反推荐:

不要使用Test::UnitPerl 测试包系列,例如Test::Unit::AssertTest::Unit::TestCases

原因:Test::Unit似乎被遗弃了。

Test::Unit、Test::Unit::TestCases、Test::Unit::Assert 工作得很好(当我在 2015-2016 年使用它们时)。Test::Unit 据说没有与 Perl 的 Test Anything Protocol (TAP) 集成,尽管我发现这很容易修复。

但是 Test::Unit 令人沮丧,因为许多其他 Perl 测试包大多使用 Test::Builder 构建,例如 Test::More、Test::Most、Test::Exception、Test::Differences、Test::Deep 、Test::Warn 等与 Test::Unit 的面向对象测试方法不能很好地交互。

一旦您将 Test::Unit 调整为与 Test::More 和 TAP 一起使用,您就可以混合 Test::Unit 测试和 Test::Builder 测试;但这些其他包的良好特性无法用于 OO 扩展。无论如何,这就是使用 xUnit 样式测试的主要原因。

据说 CPANTest::Class允许“以 xUnit/JUnit 样式轻松创建测试类”——但我不确定我是否可以推荐这样做。对我来说,它当然不像 xUnit - 不是 OO,而是像 那样的特殊名称,is(VAL1,VAL2,TESTNAME)而不是 xUnit 样式名称$test_object->assert_equals(VAL1,VAL2,TEST_ERR_MSG)。Test::Class 确实具有自动检测注释为 :Test 的所有测试的令人愉快的功能,与 xUnit 和 TEST::Unit::TestCase 使用内省运行所有名为 test_* 的函数的方法相当。

然而,底层包Test::Builder是面向对象的,因此更具 xUnit 风格。不要被这个名字吓到——它不是一个工厂,它主要是一个带有测试断言方法的套件。尽管大多数人继承自它,但如果您愿意,您可以直接调用它,例如$test_object->is(VAL1,VAL2,TESTNAME),并且通常您可以使用 Test::Builder 调用来解决程序包的限制,例如构建在 Test:: 之上的 Test::More 。 Builder - 例如修复报告错误的调用堆栈级别。

Test::Builder 通常使用单例样式,但可以创建多个对象。我不确定这些行为是否符合 xUnit 系列测试的预期。

到目前为止,没有简单的方法可以解决限制,例如 Perl TAP 测试使用 TEST_NAMES,每个断言,没有层次结构,并且不区分 TEST_NAMES 和 TEST_ERROR_MESSAGES。(错误报告级别有助于弥补这一缺陷。)

可以创建一个适配器,使 Test::Builder 和 TAP 样式测试更加面向对象,以便您可以基于 TAP 以外的其他内容进行变基(它记录比 TAP 更有用的信息 - 据说类似于 ANT 的 XML 协议)。我认为调整名称和/或缺失的概念将涉及进入 Test::Builder 或内省。