如何最自然地模拟Perl的Test::More::done_testing?

3
我必须在一个非常旧的环境中构建单元测试,使用的是早期版本的Test::More (perl5.8,其中$Test::More::VERSION为'0.80'),它还没有添加done_testing()
由于实际原因,升级到较新的Test::More并不可行。而我试图避免使用no_tests - 不捕获单元测试过早退出的情况通常是一个坏主意 - 比如由于某些逻辑未按预期执行时。 最惯用的运行可配置数量的测试的方法是什么,假设没有使用no_testsdone_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{}和其后面。另一种方法是通过调用Test::More->builder->plan(tests=>$total_tests_calculated)来模拟done_testing()。我不确定这在惯用性方面是否更好。

2
升级Test::More的实际原因是什么? - Schwern
2
@DVK演示?写作?管理参与?为了升级Perl模块?很抱歉,我认为这些都是“不切实际的原因”。 :-/ 如果您的升级过程如此缓慢,请考虑仅针对您的项目拥有一个特定的Perl库目录,在其中安装和覆盖您的项目需要的任何额外功能和升级,而独立于其他人。 - Schwern
1
@Schwern - 我不创造这个世界,我只是生活在其中。但如果你想知道为什么它如此荒谬,请考虑一下所涉及的Perl代码库运行了大型公司的很大一部分。变更管理是一件大事。 - DVK
3
除了模块升级的政治含义之外,我建议不要试图入侵done_testing,而只是使用no_plan。你可能会花更多的时间在入侵上,调整计划次数,并引入更多的测试不稳定性,这比很少失败的no_plan错失更多的时间。当然,此时您必须将该黑客分解为模块以在多个位置使用它,此时最好拥有Test :: More的本地副本。这是您遇到的真正问题还是您一直在阅读反no_plan的宣传? - Schwern
2
有很多大公司依赖Perl,但并没有你们的政治问题。也有很多小公司存在你们的问题。问题不在企业的规模。 - brian d foy
显示剩余7条评论
4个回答

3
不要为了避免版本问题而进行无用努力,只需附带Test::More的副本即可。它没有依赖项。只需将其安装到您的分布式中的中(您可以构建它,然后复制),然后在您的测试中使用即可。

1
@DVK 因为什么被责备了?Test::More的副本是针对你的测试特定的,它不会影响任何其他人的代码。 - Schwern
1
@DVK:有趣的是,企业界的人会使用与他们拒绝的情况非常相似的案例来打击你:“如果你在这里放置一个特殊的Test::More,假设我们想要升级Test::More,依赖于它的代码可能会与其他代码不同步...等等...”,你甚至可能会在下一次评审中看到这种“缺乏理解”。 - Axeman
2
@DVK 他们不允许您升级模块,也不允许您使用本地副本,即使在测试中?您有一个问题,而且这与Test::More无关。不要让恐惧阻止您修复破损的系统,特别是如果只是政策问题。不要以Randal的案例作为不采取行动的某种正当理由,因为安装Perl模块的本地副本并不适用。反击吧。指出您正在浪费时间绕过缺失的Test::More功能并不得不编写不可靠的版本。指出真正的生产力和质量正在因为不允许使用本地副本而受到损失。 - Schwern
我能理解DVK的想法...有些人天生就对模块和库持有不信任感。不要问我为什么。 - Zaid
@Shwern - 你有没有关于Test::More及其相关组件向后兼容性的数据(我认为你是所有者)?如果它是100%向后兼容的(无论是设计上还是当前版本通过旧版本的所有单元测试),那么升级请求可能会更加顺利。 - DVK
显示剩余5条评论

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::包中,也可以使用@::test_set。同时,从List::Util中使用mapsum可以缩短BEGIN块中的代码。函数形式还可以反转数据流,这使得所有测试都可以在末尾声明,保持plan调用在顶部,以便提醒为什么首先使用了BEGIN块。


Eric - 我喜欢将某些东西放入测试包的想法。+1 - DVK

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;
}

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 }

这样至少可以使测试计数与其计算的测试块保持一致,而不是将所有内容放在一个难以维护的大块中。这些都非常明显,除了 BEGIN 之外,不需要任何魔法。

顺便说一下,Test::More 的新版本具有 subtest,可以更好地处理将测试分解为多个计划的问题。


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