以下是改进现有测试以及创建新测试的指南。
在测试开发期间使用 no_plan
是可以的,但一旦完成,或者即使您正在发送补丁/提交到存储库,也请指定您的计划。
use_ok()
检查您的 use
语句。将您的 use
语句包装在 BEGIN
块内的 use_ok()
中。
BEGIN { use_ok( "MARC::Batch" ) or die; }
or die;
修饰符不是必需的,但如果模块只编译了一部分,即使代码正确,一些测试也会失败,它可以帮助避免混淆。此外,如果模块甚至无法编译,测试它就没有意义,对吧?
isa_ok()
检查。如果您实例化一个对象,请使用 isa_ok()
检查它。
my $batch = MARC::Batch->new( 'USMARC', @files ); isa_ok( $batch, "MARC::Batch" );
构造函数是最明显的,但任何返回对象的函数都应该检查该对象是否为预期类型。
while ( my $marc = $batch->next ) { isa_ok( $marc, "MARC::Record" ); # more tests... }
在这种情况下,next()
方法应该每次都返回一个 MARC::Record
。
检查 unlink()
的返回值,然后检查文件是否确实消失了。
is( unlink( $filename ), 1, "Remove $filename" ); ok( !-e $filename, "Actually gone" );
(此习惯用法将在理想情况下使用。在 VMS 上,您可能需要多次 unlink()
同一个文件,直到它完全删除;上面的 is()
测试将仅在 $filename
实际上存在时成功,这可能不是您想要测试的。)
所有 Test::More 函数中的最后一个参数是用于测试的标签或描述。如果您编写了一个测试,您应该知道它在做什么,所以给它贴上标签。
唯一的例外是 isa_ok
和 can_ok
,因为它们非常直观,并为您提供默认消息。
所有对象相等性测试都使用 is
完成
is( $object, $object2, '... our objects are equal' );
对象不相等性测试可以使用 isnt
完成
isnt( $object, $object2, '... our objects are not equal' );
这样做的原因是我们实际上是在比较字符串化的实例,而 is
和 isnt
比较字符串。如果对象的字符串化没有提供足够的信息来有意义地比较对象,这可能会发生在重载相等或字符串化运算符(直接或间接)的情况下,找到可以做到这一点的东西(例如,使用函数 overload::StrVal()
)。
我们应该注意我们正在进行的比较中的类型。对于比较字符串,使用 is
和 isnt
,并使用 cmp_ok
进行任何数字比较或更复杂的比较,而不是相等性。只有在正确的情况下使用正确的运算符才有意义。
这也意味着看起来像这样的测试
ok( $cost == $expected_cost );
应该改写为
is( $cost, $expected_cost );
或者,更好的是,因为这些值是数字
cmp_ok( $cost, '==', $expected_cost, 'Cost calculated correctly.' );
它使测试更加明确,并利用了 is
、isnt
和 cmp_ok
的错误消息。
is_deeply
测试除标量以外的其他内容如果可能,我们应该明确检查数据结构的内容,而不是仅仅隐式测试元素/键计数或其他此类技术。
使用 is_deeply
,您可以执行以下操作
my @expected = qw( Larry Moe Curly ); my @stooges = get_stooges(); is_deeply( \@stooges, \@expected, "Got proper stooges" );
如果它们不同,您将收到一条消息,说明它们在哪里不同。
在 is_deeply
不合适的时候,Test::More
还包含三个实用程序函数 eq_array
、eq_hash
和 eq_set
,它们在调用时返回一个布尔值。这些可以像这样使用
my @expected = qw( Larry Moe Curly ); my @stooges = get_stooges(); # use eq_set to test arrays regardless of their order ok(eq_set( \@stooges, \@expected), "Got proper stooges" );
is_deeply
不适合比较重载对其实现类型进行解引用的对象。
事后测试的假设也应该事先测试。我的意思是,如果您假设在操作完成后一个变量将为真,但在之前它为假。首先确认初始的假值,然后运行您的代码,并在之后测试真值。这在某些人看来可能有点过分,但实际上第一个测试只是为了加强第二个测试。
避免(大量)使用全局变量。您不会在常规代码中这样做,那么为什么要在测试中这样做呢?
沿着这条线,应该避免这样的结构
my $item = Object->new(); ... test $item here $item = Object->new(@args); ... different tests for $item here
通常应该避免像这样重复使用变量。它只会使测试流程混乱。理想情况下,经过测试的实例应该被认为是单赋值变量,并且永远不会被重复使用。
所以现在上面变成了
my $item = Object->new(); ... test $item here my $item_w_args = Object->new(@args); ... test $item_w_args here
或者更好的是,如果你的实例之间不相互作用,就把它们放在它们自己的词法作用域中。
CONSTRUCTOR: { my $obj = Object->new(); ... } CONSTRUCTOR_WITH_ARGS: { my $obj = Object->new( foo => 'bar' ); ... }
由 Andy Lester 维护,Stevan Little 和 Rafael Garcia-Suarez 贡献。