Test::Builder - 用于构建测试库的后端
package My::Test::Module;
use base 'Test::Builder::Module';
my $CLASS = __PACKAGE__;
sub ok {
my($test, $name) = @_;
my $tb = $CLASS->builder;
$tb->ok($test, $name);
}
Test::Simple 和 Test::More 已被证明是流行的测试模块,但它们并不总是足够灵活。Test::Builder 提供了一个构建块,可以用来编写自己的测试库,这些库可以协同工作。
my $Test = Test::Builder->new;
返回一个 Test::Builder 对象,表示测试的当前状态。
由于你每个程序只运行一个测试,new
始终返回同一个 Test::Builder 对象。无论你调用多少次 new()
,你得到的都是同一个对象。这被称为单例模式。这样做是为了让多个模块共享一些全局信息,比如测试计数器和测试输出的去向。
如果你想要一个与单例模式不同的全新的 Test::Builder 对象,请使用 create
。
my $Test = Test::Builder->create;
好的,所以可以存在多个 Test::Builder 对象,这就是你获取它们的方式。如果你正在测试一个基于 Test::Builder 的模块,你可能会使用它来代替 new()
,但除此之外,你可能想要使用 new
。
注意:实现尚未完成。例如,level
仍然由所有 Test::Builder 对象共享,即使是使用此方法创建的对象。此外,方法名称将来可能会更改。
$builder->subtest($name, \&subtests, @args);
请参阅 Test::More 中 subtest
的文档。
subtest
还会(可选地)接受参数,这些参数将传递给子测试引用。
diag $builder->name;
返回当前构建器的名称。顶级构建器默认值为 $0
(可执行文件的名称)。子构建器通过 child
方法命名。如果没有提供名称,将命名为“Child of $parent->name”。
$Test->reset;
将 Test::Builder 单例重新初始化为其原始状态。主要用于在持久环境中运行的测试,其中同一个测试可能在同一个进程中多次运行。
这些方法用于设置测试并声明测试的数量。通常你只需要调用其中一个方法。
$Test->plan('no_plan');
$Test->plan( skip_all => $reason );
$Test->plan( tests => $num_tests );
一种方便的设置测试的方法。调用此方法,Test::Builder 将打印相应的标题并采取相应的操作。
如果你调用 plan()
,不要调用下面任何其他方法。
my $max = $Test->expected_tests;
$Test->expected_tests($max);
获取/设置我们期望此测试运行的测试数量,并打印出相应的标题。
$Test->no_plan;
声明此测试将运行不确定的数量的测试。
$Test->done_testing();
$Test->done_testing($num_tests);
声明你已经完成测试,从现在起不再运行任何测试。
如果尚未输出计划,它将输出计划。
$num_tests
是你计划运行的测试数量。如果已经声明了编号计划,并且与之矛盾,则将运行一个失败的测试以反映计划错误。如果声明了 no_plan
,这将覆盖它。
如果 done_testing()
被调用两次,第二次调用将发出一个失败的测试。
如果省略 $num_tests
,将使用运行的测试数量,就像 no_plan
一样。
done_testing()
实际上是在你想要使用 no_plan
但更安全的情况下使用。你可以像这样使用它
$Test->ok($a == $b);
$Test->done_testing();
或者计划可变数量的测试
for my $test (@tests) {
$Test->ok($test);
}
$Test->done_testing(scalar @tests);
$plan = $Test->has_plan
查找是否已定义计划。$plan
可以是 undef
(未设置计划)、no_plan
(测试数量不确定)或整数(预期测试数量)。
$Test->skip_all;
$Test->skip_all($reason);
使用给定的 $reason
跳过所有测试。立即以 0 退出。
my $pack = $Test->exported_to;
$Test->exported_to($pack);
告诉 Test::Builder 你将函数导出到哪个包。
此方法并不十分有用,因为共享相同 Test::Builder 对象的模块可能会导出到不同的包,并且只有最后一个包会被使用。
这些实际上运行测试,类似于 Test::More 中的函数。
如果测试通过,它们都返回 true;如果测试失败,则返回 false。
$name
始终是可选的。
$Test->ok($test, $name);
你的基本测试。如果 $test
为真,则通过;如果 $test
为假,则失败。就像 Test::Simple 的 ok()
一样。
$Test->is_eq($got, $expected, $name);
就像 Test::More 的 is()
一样。检查 $got eq $expected
是否成立。这是字符串版本。
undef
只能匹配另一个 undef
。
$Test->is_num($got, $expected, $name);
就像 Test::More 的 is()
一样。检查 $got == $expected
是否成立。这是数字版本。
undef
只能匹配另一个 undef
。
$Test->isnt_eq($got, $dont_expect, $name);
就像 Test::More 的 isnt()
一样。检查 $got ne $dont_expect
是否成立。这是字符串版本。
$Test->isnt_num($got, $dont_expect, $name);
就像 Test::More 的 isnt()
一样。检查 $got ne $dont_expect
是否成立。这是数字版本。
$Test->like($thing, qr/$regex/, $name);
$Test->like($thing, '/$regex/', $name);
就像 Test::More 的 like()
一样。检查 $thing
是否匹配给定的 $regex
。
$Test->unlike($thing, qr/$regex/, $name);
$Test->unlike($thing, '/$regex/', $name);
就像 Test::More 的 unlike()
一样。检查 $thing
是否**不匹配**给定的 $regex
。
$Test->cmp_ok($thing, $type, $that, $name);
与 Test::More 的 cmp_ok()
功能相同。
$Test->cmp_ok($big_num, '!=', $other_big_num);
这些是在编写测试过程中使用的方法,但本身并不是测试。
$Test->BAIL_OUT($reason);
指示 Test::Harness 测试进行得非常糟糕,所有测试都应该终止。这包括运行任何其他测试脚本。
它将以 255 退出。
$Test->skip;
$Test->skip($why);
跳过当前测试,报告 $why
。
$Test->todo_skip;
$Test->todo_skip($why);
与 skip()
相似,只是它会将测试声明为失败并标记为 TODO。类似于
print "not ok $tnum # TODO $why\n";
这些方法在编写自己的测试方法时很有用。
$Test->maybe_regex(qr/$regex/);
$Test->maybe_regex('/$regex/');
此方法在 Test::Builder 适用于 5.6 之前的 Perl 版本时很有用,这些版本没有 qr//。现在它几乎没有用。
用于构建接受正则表达式作为参数的测试函数的便利方法。
接受由 qr//
生成的带引号的正则表达式,或表示正则表达式的字符串。
返回一个 Perl 值,该值可用于代替相应的正则表达式,如果其参数无法识别,则返回 undef
。
例如,like()
的一个版本,没有有用的诊断消息,可以写成
sub laconic_like {
my ($self, $thing, $regex, $name) = @_;
my $usable_regex = $self->maybe_regex($regex);
die "expecting regex, found '$regex'\n"
unless $usable_regex;
$self->ok($thing =~ m/$usable_regex/, $name);
}
my $is_fh = $Test->is_fh($thing);
确定给定的 $thing
是否可以用作文件句柄。
$Test->level($how_high);
当报告测试失败的位置时,$Test
应该在调用堆栈中向上查找多远。
默认为 1。
设置 $Test::Builder::Level
会覆盖。这通常在本地化时很有用
sub my_ok {
my $test = shift;
local $Test::Builder::Level = $Test::Builder::Level + 1;
$TB->ok($test);
}
为了礼貌地对待包装你自己的其他函数,你通常希望递增 $Level
而不是将其设置为常量。
$Test->use_numbers($on_or_off);
测试是否应该输出数字。也就是说,如果为真
ok 1
ok 2
ok 3
或如果为假
ok
ok
ok
当您无法依赖测试输出顺序时,这非常有用,例如当涉及线程或分叉时。
默认为开启。
$Test->no_diag($no_diag);
如果设置为真,则不会打印任何诊断信息。这包括对 diag()
的调用。
$Test->no_ending($no_ending);
通常,Test::Builder 在测试结束时会进行一些额外的诊断。它还会更改退出代码,如下所述。
如果为真,则不会执行任何操作。
$Test->no_header($no_header);
如果设置为真,则不会打印“1..N”标题。
控制测试输出的位置。
您的测试可以更改 STDOUT 和 STDERR 的指向位置,Test::Builder 的默认输出设置不会受到影响。
$Test->diag(@msgs);
打印给定的 @msgs
。类似于 print
,参数只是简单地附加在一起。
通常,它使用 failure_output()
处理程序,但如果这是针对 TODO 测试,则使用 todo_output()
处理程序。
输出将缩进并用 # 标记,以避免干扰测试输出。如果末尾没有换行符,则会添加一个换行符。
我们鼓励使用此方法,而不是直接调用 print。
返回 false。为什么?因为 diag()
通常与失败的测试一起使用 (ok() || diag()
),它会“传递”失败。
return ok(...) || diag(...);
$Test->note(@msgs);
类似于 diag()
,但它打印到 output()
处理程序,因此用户通常不会看到它,除非处于详细模式。
my @dump = $Test->explain(@msgs);
将以人类可读的格式转储任何引用的内容。对于以下情况非常有用...
is_deeply($have, $want) || diag explain $have;
或
is_deeply($have, $want) || note explain $have;
my $filehandle = $Test->output;
$Test->output($filehandle);
$Test->output($filename);
$Test->output(\$scalar);
这些方法控制 Test::Builder 在哪里打印其输出。它们接受一个打开的 $filehandle
、一个要打开并写入的 $filename
或一个要追加的 $scalar
引用。它始终返回一个 $filehandle
。
output 是正常“ok/not ok”测试输出的位置。
默认为 STDOUT。
failure_output 是测试失败和 diag()
的诊断输出的位置。它通常不会被 Test::Harness 读取,而是显示给用户。
默认为 STDERR。
todo_output
用于代替 failure_output()
,用于失败 TODO 测试的诊断。用户不会看到这些信息。
默认为 STDOUT。
$tb->reset_outputs;
将所有输出文件句柄重置为默认值。
$tb->carp(@message);
使用 @message
警告,但消息将显示为来自原始测试函数被调用的位置($tb->caller
)。
$tb->croak(@message);
使用 @message
退出,但消息将显示为来自原始测试函数被调用的位置($tb->caller
)。
这将关闭结果的长期存储。调用此方法将使 details
和 summary
无效。如果运行的测试数量足以填满所有可用内存,则可能需要使用此方法。
Test::Builder->new->no_log_results();
无法将其重新打开。
my $curr_test = $Test->current_test;
$Test->current_test($num);
获取/设置当前正在进行的测试编号。通常不需要设置此项。
如果向前设置,则缺少的测试的详细信息将被填充为“未知”。如果向后设置,则中间测试的详细信息将被删除。如果你真的想,可以擦除历史记录。
my $ok = $builder->is_passing;
指示测试套件当前是否通过。
更正式地说,如果发生任何导致测试套件无法通过的情况,它将为假。否则为真。
例如,如果没有任何测试运行,is_passing()
将为真,因为即使没有测试的套件是失败的,你也可以向其中添加一个通过的测试并开始通过。
不要想太多。
my @tests = $Test->summary;
到目前为止测试的简单摘要。通过为真,失败为假。这是一个逻辑上的通过/失败,所以待办事项是通过的。
当然,测试 #1 是 $tests[0],等等...
my @tests = $Test->details;
与 summary()
相似,但包含更多详细信息。
$tests[$test_num - 1] =
{ 'ok' => is the test considered a pass?
actual_ok => did it literally say 'ok'?
name => name of the test (if any)
type => type of test (if any, see below).
reason => reason for the above (if any)
};
'ok' 如果 Test::Harness 将认为测试通过,则为真。
'actual_ok' 反映测试是否确实打印了 'ok' 或 'not ok'。这是为了检查 'todo' 测试的结果。
'name' 是测试的名称。
'type' 指示它是否是特殊测试。普通测试的类型为 ''。类型可以是以下之一
skip see skip()
todo see todo()
todo_skip see todo_skip()
unknown see below
有时 Test::Builder 测试计数器会在没有打印任何测试输出的情况下递增,例如,当 current_test()
更改时。在这些情况下,Test::Builder 不知道测试的结果,因此其类型为 'unknown'。这些测试的详细信息将被填充。它们被认为是 ok,但名称和 actual_ok 被保留为 undef
。
例如,“not ok 23 - hole count # TODO insufficient donuts” 将导致以下结构
$tests[22] = # 23 - 1, since arrays start from 0.
{ ok => 1, # logically, the test passed since its todo
actual_ok => 0, # in absolute terms, it failed
name => 'hole count',
type => 'todo',
reason => 'insufficient donuts'
};
my $todo_reason = $Test->todo;
my $todo_reason = $Test->todo($pack);
如果当前测试被认为是“TODO”,它将返回原因(如果有)。此原因可以来自 $TODO
变量或最后一次调用 todo_start()
。
由于 TODO 测试不需要原因,因此即使在 TODO 块内,此函数也可以返回空字符串。使用 $Test->in_todo
来确定您当前是否在 TODO 块内。
todo()
旨在找到正确的包来查找 $TODO
。它非常擅长猜测要查看的正确包。它首先根据 $Level + 1
查找调用者,因为 todo()
通常在测试函数内调用。作为最后的手段,它将使用 exported_to()
。
有时会对 todo()
应该在哪里查找 $TODO
变量感到困惑。如果您想确定,请明确告诉它要使用哪个 $pack。
my $todo_reason = $Test->find_TODO();
my $todo_reason = $Test->find_TODO($pack);
与 todo()
相似,但只返回 $TODO
的值,忽略 todo_start()
。
也可以用来将 $TODO
设置为新值,同时返回旧值
my $old_reason = $Test->find_TODO($pack, 1, $new_reason);
my $in_todo = $Test->in_todo;
如果测试当前在 TODO 块内,则返回 true。
$Test->todo_start();
$Test->todo_start($message);
此方法允许您将所有后续测试声明为 TODO 测试,直到调用 todo_end
方法为止。
TODO:
和 $TODO
语法通常非常擅长确定我们是否在 TODO 测试中。但是,我们经常发现这无法确定(例如,当我们想使用 $TODO
但测试在其他无法事先推断的包中执行时)。
请注意,您可以使用此方法嵌套“todo”测试
$Test->todo_start('working on this');
# lots of code
$Test->todo_start('working on that');
# more code
$Test->todo_end;
$Test->todo_end;
这通常不建议这样做,但大型测试系统通常有奇怪的内部需求。
我们尝试让它也适用于 TODO: 语法,但不能保证,并且也不鼓励使用它
TODO: {
local $TODO = 'We have work to do!';
$Test->todo_start('working on this');
# lots of code
$Test->todo_start('working on that');
# more code
$Test->todo_end;
$Test->todo_end;
}
为了安全起见,选择一种“TODO”风格。
todo_end
$Test->todo_end;
停止将测试作为“TODO”测试运行。如果在没有先前的 todo_start
方法调用情况下调用此方法,则该方法将致命。
my $package = $Test->caller;
my($pack, $file, $line) = $Test->caller;
my($pack, $file, $line) = $Test->caller($height);
与普通的 caller()
相似,但它根据您的 level()
报告。
$height
将被添加到 level()
中。
如果 caller()
跑到堆栈顶端,它将报告最高上下文。
如果所有测试都通过,Test::Builder 将以零退出(这是正常的)。如果任何测试失败,它将以失败的测试数量退出。如果您运行的测试少于(或多于)计划的测试,则缺少的(或额外的)测试将被视为失败。如果从未运行过任何测试,Test::Builder 将发出警告并以 255 退出。如果测试在成功完成所有测试后死亡,它仍将被视为失败,并将以 255 退出。
因此,退出代码是...
0 all tests successful
255 test died or all passed but wrong # of tests run
any other number how many failed (including missing or extras)
如果您失败的测试超过 254 个,它将被报告为 254。
在 perl 5.8.1 及更高版本中,Test::Builder 是线程安全的。测试编号由所有线程共享。这意味着如果一个线程使用 current_test()
设置测试编号,它们都会受到影响。
虽然 5.8.1 之前的版本有线程,但它们包含太多错误而无法支持。
只有在 在 Test::Builder 之前 加载 threads.pm 时,Test::Builder 才是线程感知的。
您可以使用以下方法之一直接禁用线程支持
$ENV{T2_NO_IPC} = 1
或
no Test2::IPC;
或
Test2::API::test2_ipc_disable()
对于您执行的每个测试,都会存储一个信息性哈希,可以通过 details()
访问。因此,内存使用量将随着每个测试运行线性增长。虽然这对大多数测试套件来说不是问题,但如果您在同一运行中执行大量(数十万到百万)组合测试,则可能会成为问题。
在这种情况下,建议您将测试文件拆分成更小的文件,或者使用反向方法,执行“正常”(代码)比较,并在出现任何意外情况时触发 fail()
。
未来版本的 Test::Builder 将提供一种关闭历史记录的方法。
CPAN 可以提供最好的示例。 Test::Simple、Test::More、Test::Exception 和 Test::Differences 都使用 Test::Builder。
原始代码由 chromatic 编写,由 Michael G Schwern <[email protected]> 维护。
版权所有 2002-2008 chromatic <[email protected]> 和 Michael G Schwern <[email protected]>。
本程序是自由软件;您可以根据与 Perl 本身相同的条款重新发布和/或修改它。
参见 https://perldotcom.perl5.cn/perl/misc/Artistic.html