我必须在环境中使用非常旧版本的Test::More
(带有$Test::More::VERSION being '0.80'
的perl5.8)构建单元测试,该版本早于添加done_testing()
。
由于实际原因,升级到更新的测试::更多是不可能的。我试图避免使用no_tests
- 当你的单元测试过早退出时通常是一个坏主意 - 比如由于某些逻辑没有按你预期的那样执行。
运行可配置数量的测试的最常用方法是什么,假设没有使用no_tests
或done_testing()
?
详细:
我的单元测试通常采用以下形式:
use Test::More; my @test_set = ( [ "Test #1", $param1, $param2, ... ] ,[ "Test #1", $param1, $param2, ... ] # ,... ); foreach my $test (@test_set) { run_test($test); } sub run_test { # $expected_tests += count_tests($test); ok(test1($test)) || diag("Test1 failed"); # ... }
use Test::More tests => 23;
或BEGIN {plan tests => 23}
的标准方法不起作用,因为两者都明显在@tests
知道之前执行。
我目前的做法是让@tests
全局并在BEGIN {}
块中定义它,如下所示:
use Test::More; BEGIN { our @test_set = (); # Same set of tests as above my $expected_tests = 0; foreach my $test (@tests) { my $expected_tests += count_tests($test); } plan tests => $expected_tests; } our @test_set; # Must do!!! Since first "our" was in BEGIN's scope :( foreach my $test (@test_set) { run_test($test); } # Same sub run_test {} # Same
我觉得这可以更具惯用性但不确定如何改进。气味中最重要的是重复our @test_test
声明 - 在BEGIN{}
之后及之后。
另一种方法是通过调用done_testing()
来模拟Test::More->builder->plan(tests=>$total_tests_calculated)
。我不确定它是否更具有惯用性。
答案 0 :(得分:3)
不要破解旧版本,只需附带一份Test :: More。它没有依赖关系。只需将其安装到您的发行版的t/lib
(您可以构建它,然后复制blib/lib
),然后在测试中use lib "t/lib"
。
答案 1 :(得分:1)
这是一种相当惯用的方法:
use warnings;
use strict;
use Test::More;
use List::Util 'sum';
sub count_tests {1}
BEGIN {
plan tests => sum map {
count_tests($_)
} @test::set = (
[ "Test #1", '$param1, $param2, ...' ],
[ "Test #1", '$param1, $param2, ...' ],
)
}
run_test($_) for @test::set;
使用完全限定名称可以避免使用our
,如果您担心在@::test_set
包中添加某些内容,也可以使用test::
。使用map
中的sum
和List::Util
可以缩短BEGIN
块中的代码。函数形式还反转了数据流,允许在最后声明所有测试,将plan
调用保持在顶部,以提醒为什么BEGIN
块在第一个中被使用的地方。
答案 2 :(得分:1)
如何使用闭包来返回测试集,这可以避免包变量的尴尬?这是一个例子:
use strict;
use warnings;
use Test::More;
BEGIN {
my @ts = (
[ 'Test 1', 1, 1 ],
[ 'Test 2', 3, 3 ],
);
plan tests => scalar @ts;
sub test_sets { return @ts }
}
for my $ts ( test_sets() ){
run_test($ts);
}
sub run_test {
my ($msg, $val, $exp) = @{shift()};
is $val, $exp, $msg;
}
答案 3 :(得分:1)
如果您只需要根据测试表计算计划,那就太微不足道了。
use Test::More;
my $Asserts_Per_Set = 10;
my %Tests = (
"Test #1" => { foo => "bar", this => "that" },
"Test #2" => { foo => "yar", this => 42 },
...
);
plan tests => keys %Tests * $Asserts_Per_Set;
for my $name (keys %Tests) {
run_tests($name, $Tests{$name});
}
如果由于某种原因run_tests
需要根据数据运行可变数量的测试,请使用skip
而不是if
,以便始终运行一致数量的测试
SKIP: {
skip "Can't run foo test on frobnitz", 2 if $test->{foo} and $test->{frobnitz};
is foo(), $test->{foo};
is bar(), $test->{foo} + 9;
}
对于更复杂的事情,请使用BEGIN
块添加到计划中。
use Test::More;
my $Count;
BEGIN { $Count += X }
...run X tests...
BEGIN { $Count += Y }
...run Y tests...
BEGIN { plan tests => $Count }
这至少使测试计数计算与其计算的测试块保持一致,而不是将其全部放在顶部的一个大的不可维护的blob中。除了BEGIN
之外,它都是高度可见的并且不需要任何魔法。
顺便提一下,新版本的Test :: More有subtest
来更好地解决将测试分解为多个计划的问题。