[英]In Perl, what is the most reliable way to determine a coderef's package?
[英]What is the most idiomatic way to emulating Perl's Test::More::done_testing?
我必須在環境中構建單元測試,其中包含一個非常舊版本的Test::More
(perl5.8,其中$Test::More::VERSION being '0.80'
),它早於添加done_testing()
。
由於實際原因,升級到更新的Test :: More是不可能的。 我試圖避免使用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;
方法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()
Test::More->builder->plan(tests=>$total_tests_calculated)
。 我不確定它是否更具有慣用性。
不要破解舊版本,只需附帶Test :: More的副本。 它沒有依賴關系。 只需將其安裝到您的發行版的t/lib
中(您可以構建它然后復制blib/lib
),然后在測試中use lib "t/lib"
。
這是一個相當慣用的方法:
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::
package中添加某些東西,也可以使用@::test_set
。 使用List::Util
map
和sum
可以縮短BEGIN
塊中的代碼。 函數形式還反轉了數據流,允許在最后聲明所有測試,將plan
調用保持在頂部,以提醒為什么首先使用BEGIN
塊。
如何使用閉包來返回測試集,這可以避免包變量的尷尬? 這是一個例子:
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;
}
如果您只需要根據測試表計算計划,那就太微不足道了。
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
來更好地處理將測試分解為多個計划的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.