内容

名称

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.tt/bar.tt/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) } );
}

TEST TYPES

ONFAIL

BEGIN { plan test => 4, onfail => sub { warn "CALL 911!" } }

虽然测试失败应该足够了,但可以在测试运行结束时触发额外的诊断信息。onfail 传递一个哈希引用数组,描述每个测试失败。每个哈希至少包含以下字段:packagerepetitionresult。(你不应该依赖于其他任何字段的存在。)如果测试具有预期值或诊断(或“注释”)字符串,这些也将被包含在内。

可选onfail 钩子可以简单地用于打印出软件包的版本和/或如何报告问题。它也可以用于生成针对特定怪异测试失败的极其复杂的诊断信息。但是它不是万能药。核心转储或其他不可恢复的错误会阻止 onfail 钩子运行。(它是在 END 块中运行的。)此外,onfail 在大多数情况下可能过于复杂。(你的测试代码应该比它正在测试的代码更简单,对吧?)

BUG 和注意事项

环境

如果设置了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::MoreTest::Unit.

这个模块的一些早期版本在skip(...)的描述中有一些令人困惑的错别字。

参见

Test::Harness

Test::SimpleTest::MoreDevel::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 本身相同的条款下使用、重新分发和/或修改。