Test - 提供一個用於撰寫測試腳本的簡單架構
use strict;
use Test;
# use a BEGIN block so we print our plan before MyModule is loaded
BEGIN { plan tests => 14, todo => [3,4] }
# load your module...
use MyModule;
# Helpful notes. All note-lines must start with a "#".
print "# I'm testing MyModule version $MyModule::VERSION\n";
ok(0); # failure
ok(1); # success
ok(0); # ok, expected failure (see todo list, above)
ok(1); # surprise success!
ok(0,1); # failure: '0' ne '1'
ok('broke','fixed'); # failure: 'broke' ne 'fixed'
ok('fixed','fixed'); # success: 'fixed' eq 'fixed'
ok('fixed',qr/x/); # success: 'fixed' =~ qr/x/
ok(sub { 1+1 }, 2); # success: '2' eq '2'
ok(sub { 1+1 }, 3); # failure: '2' ne '3'
my @list = (0,0);
ok @list, 3, "\@list=".join(',',@list); #extra notes
ok 'segmentation fault', '/(?i)success/'; #regex match
skip(
$^O =~ m/MSWin/ ? "Skip if MSWin" : 0, # whether to skip
$foo, $bar # arguments just like for ok(...)
);
skip(
$^O =~ m/MSWin/ ? 0 : "Skip unless MSWin", # whether to skip
$foo, $bar # arguments just like for ok(...)
);
此模組簡化了為 Perl 模組撰寫測試檔案的任務,其輸出格式為 Test::Harness 預期看到的格式。
若要為您的新模組(可能尚未完成)撰寫測試,請建立一個名為 t/test.t 的新檔案(在新的 t 目錄中)。如果您有多個測試檔案,要測試「foo」、「bar」和「baz」功能集,則可以將您的檔案命名為 t/foo.t、t/bar.t 和 t/baz.t
此模組定義了三個公用函式,plan(...)
、ok(...)
和 skip(...)
。預設情況下,這三個函式都由 use Test;
陳述式匯出。
plan(...)
BEGIN { plan %theplan; }
這應該是您在測試指令碼中呼叫的第一個函式。它宣告您的測試計畫,將會有多少個測試,是否允許其中任何一個測試失敗,等等。
典型的用法如下
use Test;
BEGIN { plan tests => 23 }
以下是您可以在 plan 參數中放入的內容
tests => 數字
指令碼中的測試數。這表示所有 ok() 和 skip() 呼叫。
todo => [1,5,14]
允許失敗的測試清單的參考。請參閱 "TODO TESTS"。
onfail => 子常式 { ... }
onfail => \&some_sub
如果任何測試失敗,則在測試指令碼結束時執行的子常式參考。請參閱 "ONFAIL"。
您必須呼叫 plan(...)
一次且僅一次。您應該在 BEGIN {...}
區塊中呼叫它,如下所示
BEGIN { plan tests => 23 }
ok(...)
ok(1 + 1 == 2);
ok($have, $expect);
ok($have, $expect, $diagnostics);
此函式是 Test
存在的理由。它是處理列印「ok
」或「not ok
」的基本函式,以及目前的測試編號。(這是 Test::Harness
想看到的。)
在最基本的用法中,ok(...)
僅接受單一純量表達式。如果其值為 true,則測試通過;如果為 false,則測試失敗。範例
# Examples of ok(scalar)
ok( 1 + 1 == 2 ); # ok if 1 + 1 == 2
ok( $foo =~ /bar/ ); # ok if $foo contains 'bar'
ok( baz($x + $y) eq 'Armondo' ); # ok if baz($x + $y) returns
# 'Armondo'
ok( @a == @b ); # ok if @a and @b are the same
# length
表達式在純量內容中評估。因此,以下內容將有效
ok( @stuff ); # ok if @stuff has any
# elements
ok( !grep !defined $_, @stuff ); # ok if everything in @stuff
# is defined.
一個特例是,如果表達式是子常式參考(在 sub {...}
語法或 \&foo
語法中)。在這種情況下,它會被執行,其值(true 或 false)決定測試是否通過或失敗。例如,
ok( sub { # See whether sleep works at least passably
my $start_time = time;
sleep 5;
time() - $start_time >= 4
});
在其兩個引數形式中,ok(arg1, arg2)
比較兩個純量值,看看它們是否匹配。如果兩者都未定義,或者如果 arg2 是與 arg1 匹配的正規表示式,或者如果它們與 eq
相等,則它們會匹配。
# Example of ok(scalar, scalar)
ok( "this", "that" ); # not ok, 'this' ne 'that'
ok( "", undef ); # not ok, "" is defined
如果第二個引數是正規表示式物件或看起來像正規表示式的字串,則會將其視為正規表示式。正規表示式物件使用 perl 最近版本的 qr// 算子建構。如果字串的第一個和最後一個字元是「/」,或者如果第一個字元是「m」,而其第二個和最後一個字元都是相同的非字母數字非空白字元,則會將字串視為看起來像正規表示式。這些正規表示式
正規表示式範例
ok( 'JaffO', '/Jaff/' ); # ok, 'JaffO' =~ /Jaff/
ok( 'JaffO', 'm|Jaff|' ); # ok, 'JaffO' =~ m|Jaff|
ok( 'JaffO', qr/Jaff/ ); # ok, 'JaffO' =~ qr/Jaff/;
ok( 'JaffO', '/(?i)jaff/ ); # ok, 'JaffO' =~ /jaff/i;
如果任一(或兩者!)是子常式參考,則會執行它並將其用作比較的值。例如
ok sub {
open(OUT, '>', 'x.dat') || die $!;
print OUT "\x{e000}";
close OUT;
my $bytecount = -s 'x.dat';
unlink 'x.dat' or warn "Can't unlink : $!";
return $bytecount;
},
4
;
上述測試將兩個值傳遞給 ok(arg1, arg2)
-- 第一個是 coderef,第二個是數字 4。在 ok
比較它們之前,它會呼叫 coderef,並使用其回傳值作為此參數的實際值。假設 $bytecount
回傳 4,ok
最終會測試 4 eq 4
。由於為真,因此此測試通過。
最後,您可以在 ok(arg1,arg2, note)
中附加一個選擇性的第三個參數,其中 note 是如果測試失敗時將列印的字串值。這應該是關於測試的一些有用資訊,與測試失敗的原因有關,和/或測試說明。例如
ok( grep($_ eq 'something unique', @stuff), 1,
"Something that should be unique isn't!\n".
'@stuff = '.join ', ', @stuff
);
很不幸地,無法將註解用於 ok()
的單一參數樣式。也就是說,如果您嘗試 ok(arg1, note)
,則 Test
會將其解釋為 ok(arg1, arg2)
,並可能最終測試 arg1 eq arg2
-- 這並非您想要的!
上述所有特殊情況偶爾會造成一些問題。請參閱 "BUGS and CAVEATS"。
skip(skip_if_true, args...)
這用於在某些條件下可以略過的測試。它基本上等於
if( $skip_if_true ) {
ok(1);
} else {
ok( args... );
}
...除了 ok(1)
不僅發出 "ok testnum
" 實際上發出 "ok testnum # skip_if_true_value
"。
如果此測試未略過,則 skip_if_true 之後的參數會提供給 ok(...)
。
範例用法
my $if_MSWin =
$^O =~ m/MSWin/ ? 'Skip if under MSWin' : '';
# A test to be skipped if under MSWin (i.e., run except under
# MSWin)
skip($if_MSWin, thing($foo), thing($bar) );
或者,反過來
my $unless_MSWin =
$^O =~ m/MSWin/ ? '' : 'Skip unless under MSWin';
# A test to be skipped unless under MSWin (i.e., run only under
# MSWin)
skip($unless_MSWin, thing($foo), thing($bar) );
需要記住的棘手之處是,如果您想要略過測試,則第一個參數為真,而不是執行測試;它也兼作關於略過原因的註解。因此,在上述第一個程式碼區塊中,將程式碼讀為 "如果 MSWin,則略過 -- (否則) 測試 thing($foo)
是否為 thing($bar)
" 或對於第二個案例,"除非 MSWin,否則略過..."。
此外,當您的 skip_if_reason 字串為真時,它確實應該 (為了與舊版 Test.pm 版本向後相容) 以 "Skip" 字串開頭,如上述範例所示。
請注意,在上述情況中,會評估 thing($foo)
和 thing($bar)
-- 但只要 skip_if_true
為真,則我們 skip(...)
僅會丟棄它們的值 (即,不費心將它們視為 ok(...)
的值。但是,如果您需要在略過測試時不評估參數,請使用此格式
skip( $unless_MSWin,
sub {
# This code returns true if the test passes.
# (But it doesn't even get called if the test is skipped.)
thing($foo) eq thing($bar)
}
);
甚至使用此格式,它基本上是等效的
skip( $unless_MSWin,
sub { thing($foo) }, sub { thing($bar) }
);
也就是說,兩者都是這樣
if( $unless_MSWin ) {
ok(1); # but it actually appends "# $unless_MSWin"
# so that Test::Harness can tell it's a skip
} else {
# Not skipping, so actually call and evaluate...
ok( sub { thing($foo) }, sub { thing($bar) } );
}
一般測試
預期這些測試會成功。通常,您的大多數或所有測試都屬於此類別。如果一般測試未成功,則表示有些事情錯誤。
略過的測試
skip(...)
函式用於可能無法執行的測試,具體取決於特定於平台的功能是否可用。如果未提供所需功能,第一個參數應評估為真 (想成 "是的,請略過")。在第一個參數之後,skip(...)
的工作方式與 ok(...)
完全相同。
TODO 測試
TODO 測試旨在維護一個可執行的 TODO 清單。這些測試預期會失敗。如果 TODO 測試成功,那麼有問題的功能就不應該在 TODO 清單上,對吧?
不應發布通過 TODO 測試的套件。一旦 TODO 測試開始運作,就應將其提升為一般測試,並在發布說明或變更記錄中記錄新運作的功能。
BEGIN { plan test => 4, onfail => sub { warn "CALL 911!" } }
儘管測試失敗就足夠了,但在測試執行結束時可以觸發額外的診斷。onfail
傳遞一個雜湊參考陣列,其中描述每個測試失敗。每個雜湊至少包含下列欄位:package
、repetition
和 result
。(你不應依賴任何其他欄位。)如果測試有預期值或診斷(或「註解」)字串,也會包含這些值。
選用的 onfail
鉤子可以用來列印套件版本和/或如何報告問題。它也可以用來為特別奇怪的測試失敗產生極其複雜的診斷。然而,它並非萬靈丹。核心傾印或其他無法復原的錯誤會阻止 onfail
鉤子執行。(它在 END
區塊內執行。)此外,在多數情況下,onfail
可能過於強大。(你的測試程式碼應該比它所測試的程式碼更簡單,對吧?)
ok(...)
對看起來像正規表示式的字串的特殊處理也會導致意外行為。一個無害的
ok( $fileglob, '/path/to/some/*stuff/' );
會失敗,因為 Test.pm 將第二個參數視為正規表示式!最保險的做法是使用單一參數形式
ok( $fileglob eq '/path/to/some/*stuff/' );
ok(...)
使用字串 eq
有時會在比較數字時造成奇怪的問題,特別是你將字串轉換為數字時
$foo = "1.0";
ok( $foo, 1 ); # not ok, "1.0" ne 1
最保險的做法是使用單一參數形式
ok( $foo == 1 ); # ok "1.0" == 1
正如你從上述文件和範例中推斷的,ok
的原型是 ($;$$)
(順便一提,skip
的原型是 ($;$$$)
)。這表示,例如,你可以執行 ok @foo, @bar
來比較兩個陣列的大小。但不要誤以為 ok @foo, @bar
表示比較兩個陣列的內容——你只比較每個陣列的元素數目。在閱讀 ok @foo, @bar
時很容易犯這個錯誤,因此你可能希望明確說明,並改寫為 ok scalar(@foo), scalar(@bar)
。
這幾乎肯定不會執行你預期的動作
ok $thingy->can('some_method');
為什麼?因為 can
傳回一個代碼參考,表示「它可以(而且方法是這個...)」,然後 ok
看到一個代碼參考,並認為你傳遞一個函式,希望它呼叫這個函式並考慮結果的真偽!也就是說,就像
ok $thingy->can('some_method')->();
你可能希望改為這樣
ok $thingy->can('some_method') && 1;
如果 can
傳回 false,則會傳遞給 ok
。如果傳回 true,則較大的運算式 $thingy->can('some_method') && 1
會傳回 1,而 ok
會將其視為成功的一個簡單訊號,正如您所預期的那樣。
skip
的語法幾乎是唯一可行的,但它仍然相當令人困惑。只要從上述範例開始,您就會沒問題。
此外,使用者可能會預期此
skip $unless_mswin, foo($bar), baz($quux);
在跳過測試時不會評估 foo($bar)
和 baz($quux)
。但實際上,它們會被評估,但如果 $unless_mswin
為 true,skip
就不會費心比較它們。
您可以執行此操作
skip $unless_mswin, sub{foo($bar)}, sub{baz($quux)};
但這並不好看。從長遠來看,您可能會發現像這樣做事情更簡單或更清楚
if( $^O =~ m/MSWin/ ) {
print "# Yay, we're under $^O\n";
ok foo($bar), baz($quux);
ok thing($whatever), baz($stuff);
ok blorp($quux, $whatever);
ok foo($barzbarz), thang($quux);
} else {
print "# Feh, we're under $^O. Watch me skip some tests...\n";
for(1 .. 4) { skip "Skip unless under MSWin" }
}
但請務必確認在第一個區塊中呼叫 ok
的次數與在第二個區塊中呼叫 skip
的次數完全相同。
如果設定了 PERL_TEST_DIFF
環境變數,它將被用作比較意外的多行結果的指令。如果您已安裝 GNU diff,您可能想將 PERL_TEST_DIFF
設定為 diff -u
。如果您沒有合適的程式,您可能會安裝 Text::Diff
模組,然後將 PERL_TEST_DIFF
設定為 perl -MText::Diff -e 'print diff(@ARGV)'
。如果未設定 PERL_TEST_DIFF
但 Algorithm::Diff
模組可用,則會使用它來顯示多行結果中的差異。
此模組的過去開發人員曾說過,它不再被積極開發。然而,關於它消亡的謠言被大大誇大了。非常歡迎回饋和建議。
請注意,此模組的主要價值在於其簡單性。請注意,已經有更具野心的模組,例如 Test::More 和 Test::Unit。
此模組的一些早期版本的文件在 skip(...)
的說明中有一些令人困惑的錯字。
Test::Simple、Test::More、Devel::Cover
Test::Builder 用於建立您自己的測試程式庫。
Test::Unit 是有趣的 XUnit 風格測試程式庫。
Test::Inline 讓您可以在程式碼中嵌入測試。
版權所有 (c) 1998-2000 Joshua Nathaniel Pritikin。
版權所有 (c) 2001-2002 Michael G. Schwern。
版權所有 (c) 2002-2004 Sean M. Burke。
目前維護者:Jesse Vincent。<jesse@bestpractical.com>
此套件為免費軟體,並以「原樣」提供,不提供明示或暗示的擔保。它可以根據與 Perl 相同的條款使用、重新分發和/或修改。