【发布时间】:2021-06-16 04:44:10
【问题描述】:
我对 Perl 有点陌生,我想知道是否有首选的单元测试框架?
Google 向我展示了一些不错的结果,但由于我是新手,我不知道社区内是否有明确的偏好。
【问题讨论】:
标签: perl unit-testing
我对 Perl 有点陌生,我想知道是否有首选的单元测试框架?
Google 向我展示了一些不错的结果,但由于我是新手,我不知道社区内是否有明确的偏好。
【问题讨论】:
标签: perl unit-testing
Perl 附带了大量出色的测试工具! Perl 核心有数以万计的自动检查,并且大部分都使用这些标准 Perl 框架。它们都使用 TAP 绑定在一起 - Test Anything Protocol。
在 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 指出的那样)与rjh 的answer 中列出的所有出色模块一起使用。事实上,因为Test::Builder--Test::More 和其他人所构建的工具(目前由 Schwern 维护......感谢 Schwern!)--如果需要,您可以从头开始构建您的 OWN 测试子例程这将适用于所有其他测试框架。在我看来,仅此一点就使 Perl 的 TAP 系统成为最好的系统之一:一切都可以协同工作,每个人都使用相同的工具,而且您只需很少的额外工作即可添加到框架以满足您的需求。
【讨论】:
use Test::More 'no_plan' 或调用done_testing()。
perl -v 将为您提供 perl 版本,您可以从 perl -MTest::More -e "print Test::More->VERSION()" 获得 Test::more 无论哪种方式,如果可以的话,最好有一个计划。一开始我发现它很乏味,但后来当测试意外中途死亡并且没有报告我的几个测试用例时,我发现了它的救命稻草。如果不是计划,我永远不会知道有问题。
use Test::More; 最后是 done_testing; 应该可以正常工作。否则,你需要做use Test::More qw(no_plan)。
Test::More 会告诉你。例如,如果您在脚本中添加另一个测试而不更改顶部的 tests 声明,它将显示类似 # Looks like you planned 3 tests but ran 4. 的内容,因此更改它很简单。我还认为,单元测试的全部意义在于“硬编码”要尝试的值。如果您的单元测试每次都运行可变数量的步骤,那么现在它的可重复性不是很高,是吗? :)
Test:: 模块都可以协同工作。 Test::More可以与Test::Differences和Test::Deep等组合。
Perl 最流行的测试“框架”是一种称为 TAP(测试任何协议)的测试结果格式,它是一组字符串,如下所示:
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 模块 - 这里有一些示例,但还有很多 (many):
Test::Exception - 测试您的代码是否抛出错误/不抛出任何错误
Test::Warn - 测试您的代码是否产生警告
Test::Deep - 深入比较对象。它们不必相同 - 您可以忽略数组排序、使用正则表达式、忽略对象类等。
Test::Pod - 确保您的脚本有 POD(文档),并且它是有效的
@ 987654328@ - 确保您的 POD 记录了模块中的所有方法/功能
Test::DBUnit - 测试数据库交互
Test::MockObject - 制作假装对象来控制测试环境
【讨论】:
一定要从这个页面开始:http://perldoc.perl.org/Test/Simple.html 并遵循对Test::Tutorial 的引用。
【讨论】:
如果您练习 TDD,您会注意到您的单元测试集发生了很大变化。 Test::Class 遵循 xUnit 模式 (http://en.wikipedia.org/wiki/XUnit)。
对我来说,xUnit 的主要好处是将每个测试封装在方法中。框架以测试方法的名称命名每个断言,并增加了在每次测试之前和之后运行设置和拆卸方法的可能性。
我也尝试过“perl-ish”的单元测试方式(仅使用 Test::More),但我发现它有点过时且麻烦。
【讨论】:
一些反建议可能是为了:
反推荐:
不要为 Perl 使用 Test::Unit 系列测试包,例如 Test::Unit::Assert 和 Test::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 - 不是 OO,像 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 或自省。
【讨论】: