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

32

我对 Perl 还比较陌生,想知道是否有首选的单元测试框架?

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

6个回答

41
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.';

输出将是:

ok 1 - Foo was assigned 5.
ok 2 - Bar was assigned 6.
ok 3 - Addition works correctly.

基本上,要开始工作只需要传递一个布尔值和说明发生什么的字符串!
一旦您完成此步骤,Test::More有许多其他函数可以使测试其他内容变得更加容易(字符串,正则表达式比较,深层结构比较),还有Test::Harness后端,可以让您同时测试大量单独的测试脚本。

此外,正如Schwern所指出的那样,几乎所有现代的Test::模块都可以一起使用。这意味着您可以使用Test::Class(由Markus指出)与rjhanswer中列出的所有优秀模块一起使用。实际上,因为Test::Builder——Test::More和其他模块构建在其之上(并且目前由Schwern维护...感谢Schwern!)——如果需要,您可以从头开始构建自己的测试子例程,并且它们将与所有其他测试框架一起工作。仅这一点就使得Perl的TAP系统在我看来是最好的:所有东西都可以一起工作,每个人都使用相同的工具,您可以添加到框架以满足您的需求,而只需要很少的额外工作。


6
你不必这样做。但是如果你的测试脚本在仅输出1或2个测试结果后就停止运行,Test::More会认为你的测试失败了(即使它以零错误代码退出)。你也可以使用 use Test::More 'no_plan' 或在脚本末尾调用 done_testing() - rjh
3
你使用的Perl和Test::More版本是什么? perl -v 命令可以提供Perl版本,而你可以通过 perl -MTest::More -e "print Test::More->VERSION()" 命令获取Test::More版本。无论哪种方式,如果可能的话最好有一个计划。起初我发现这很烦琐,但后来当测试在中途意外死亡时,并且没有报告我的几个测试用例时,这成为了一个救命稻草。如果不是这个计划,我将永远不会知道存在问题。 - Robert P
3
еңЁжӣҙж–°зҡ„Test::MoreпјҲ>=0.88пјүдёӯпјҢеҸӘйңҖеңЁжң«е°ҫж·»еҠ done_testing;пјҢ然еҗҺзҙ§и·ҹзқҖuse Test::More;еҚіеҸҜжӯЈеёёе·ҘдҪңгҖӮеҗҰеҲҷпјҢжӮЁйңҖиҰҒдҪҝз”Ёuse Test::More qw(no_plan)гҖӮ - rjh
3
@Mike:这只是另一种可用的安全措施;它并不是必需的。理想情况下,您已经知道运行了多少测试。实际上,“Test::More”会告诉您。例如,如果您在脚本中添加另一个测试而没有更改顶部的“tests”声明,则它将说类似于“# Looks like you planned 3 tests but ran 4.”,这使得更改变得非常简单。我还会认为单元测试的整个重点是将值“硬编码”到尝试中。如果您的单元测试每次运行可变数量的步骤,那么它现在就不太可重复了,是吧? :) - Robert P
3
值得注意的是,几乎所有现代的Test::模块都可以一起使用。例如,Test::More可以与Test::DifferencesTest::Deep等组合使用。 - Schwern
显示剩余8条评论

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

任何能够生成这些字符串的脚本都可以算作 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');

你可以使用Test::Harness(通常从shell中作为prove调用)按顺序运行一系列测试,并获取哪些测试通过或失败的摘要。
Test::More还可以执行一些更复杂的操作,例如将测试标记为TODO(不希望它们通过,但仍然运行它们)或SKIP(这些测试已经损坏/可选,不要运行它们)。您可以声明您期望运行的测试数,因此如果测试脚本在一半时死机,可以检测到。
一旦您开始进行更复杂的测试,您可能会发现其他CPAN模块有用-以下是一些示例,但有很多(许多):

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


6

5
如果你实践TDD,你会注意到你的单元测试集合会经常变动。Test::Class遵循xUnit模式(http://en.wikipedia.org/wiki/XUnit)。
对我而言,xUnit的主要好处是将每个测试封装在方法中。该框架通过测试方法的名称来命名每个断言,并添加了在每个测试之前和之后运行设置和拆卸方法的可能性。
我也尝试过使用“perl-ish”方式进行单元测试(只使用Test::More),但我觉得它有点老式和繁琐。

1
我也非常喜欢Test::Class。Test::Class的好处在于它是构建基于TAP的测试的另一种方式 - 另一个框架,将其从“过程式”转换为“面向对象”。它将“如何报告”与“如何运行”分开,让您在Test::Class对象中使用任何其他Test::More风格的测试步骤/测试函数。但它不仅限于TAP测试 - 它只是非常擅长这个领域。您可以扩展Test::Class并覆盖许多默认行为,然后简单地使用该框架。 - Robert P

5

以下是一些反建议:

反建议:

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

原因:Test::Unit似乎已被放弃。

Test::Unit、Test::Unit::TestCases、Test::Unit::Assert在2015-2016年期间使用效果相当好。虽然Test::Unit据说没有与Perl的测试任何协议(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风格测试的主要原因之一。

据说CPAN的Test::Class允许“轻松创建xUnit/JUnit风格的测试类”——但我不确定是否可以推荐这个。它肯定不像xUnit——没有面向对象,名称很奇怪,例如is(VAL1,VAL2,TESTNAME),而不是像$test_object->assert_equals(VAL1,VAL2,TEST_ERR_MSG)那样的xUnit风格名称。Test::Class具有自动检测所有标注为:Test的测试的良好特性,类似于xUnit和TEST::Unit::TestCase使用内省运行所有命名为test_*函数的方法。

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

Test::Builder通常以单例模式使用,但可以创建多个对象。我不确定这些对象是否像xUnit系列测试一样表现。

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

可能可以创建一个适配器,使Test::Builder和TAP样式的测试更加面向对象,以便您可以在除TAP之外的其他东西上重新定位(记录比TAP更有用的信息 - 据说类似于ANT的XML协议)。我认为要适应名称和/或缺失的概念将涉及进入Test::Builder或内省。


谢谢,彼得。干净多了。当我浪费时间使用老旧软件时,我会感到非常沮丧。 - Krazy Glew

0

Test2::V0是Test::More的续集...

...以及来自Test2::Harness的yath命令,是prove的续集。

有一定程度的向后兼容性,值得一看。

https://metacpan.org/pod/Test2::Manual


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