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 }
以下是可以放在计划参数中的内容
tests => number
脚本中的测试数量。这意味着所有 ok() 和 skip() 调用。
todo => [1,5,14]
对允许失败的测试列表的引用。请参阅 "TODO TESTS"。
onfail => sub { ... }
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(...)
只接受一个标量表达式。如果其值为真,则测试通过;如果为假,则测试失败。示例
# 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
语法)。在这种情况下,它将被执行,其值(真或假)决定测试通过还是失败。例如,
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)
- 第一个是代码引用,第二个是数字 4。在ok
比较它们之前,它调用代码引用,并使用其返回值作为此参数的实际值。假设$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(...)
函数用于可能或不可能运行的测试,具体取决于平台特定功能的可用性。如果所需功能不可用,则第一个参数应计算为 true(表示“是,请跳过”)。在第一个参数之后,skip(...)
的工作方式与 ok(...)
完全相同。
待办事项测试
待办事项测试旨在维护一个**可执行的待办事项列表**。这些测试预计会失败。如果待办事项测试成功,那么该功能不应该出现在待办事项列表中,对吧?
软件包不应发布带有成功待办事项测试的版本。一旦待办事项测试开始工作,它应该被提升为正常测试,并且新工作的功能应该在发行说明或变更日志中记录。
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)
。但实际上,它们确实被评估了,只是skip
在$unless_mswin
为真时不会费心比较它们。
你可以这样做
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。 <[email protected]>
此软件包是免费软件,按“原样”提供,不附带任何明示或暗示的担保。它可以在与 Perl 本身相同的条款下使用、重新分发和/或修改。