内容

名称

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::SimpleTest::More 已被证明是流行的测试模块,但它们并不总是足够灵活。Test::Builder 提供了一个构建块,可以用来编写自己的测试库,这些库可以协同工作

构造

new
my $Test = Test::Builder->new;

返回一个 Test::Builder 对象,表示测试的当前状态。

由于你每个程序只运行一个测试,new 始终返回同一个 Test::Builder 对象。无论你调用多少次 new(),你得到的都是同一个对象。这被称为单例模式。这样做是为了让多个模块共享一些全局信息,比如测试计数器和测试输出的去向。

如果你想要一个与单例模式不同的全新的 Test::Builder 对象,请使用 create

create
my $Test = Test::Builder->create;

好的,所以可以存在多个 Test::Builder 对象,这就是你获取它们的方式。如果你正在测试一个基于 Test::Builder 的模块,你可能会使用它来代替 new(),但除此之外,你可能想要使用 new

注意:实现尚未完成。例如,level 仍然由所有 Test::Builder 对象共享,即使是使用此方法创建的对象。此外,方法名称将来可能会更改。

subtest
$builder->subtest($name, \&subtests, @args);

请参阅 Test::More 中 subtest 的文档。

subtest 还会(可选地)接受参数,这些参数将传递给子测试引用。

name
diag $builder->name;

返回当前构建器的名称。顶级构建器默认值为 $0(可执行文件的名称)。子构建器通过 child 方法命名。如果没有提供名称,将命名为“Child of $parent->name”。

reset
$Test->reset;

将 Test::Builder 单例重新初始化为其原始状态。主要用于在持久环境中运行的测试,其中同一个测试可能在同一个进程中多次运行。

设置测试

这些方法用于设置测试并声明测试的数量。通常你只需要调用其中一个方法。

plan
$Test->plan('no_plan');
$Test->plan( skip_all => $reason );
$Test->plan( tests => $num_tests );

一种方便的设置测试的方法。调用此方法,Test::Builder 将打印相应的标题并采取相应的操作。

如果你调用 plan(),不要调用下面任何其他方法。

expected_tests
my $max = $Test->expected_tests;
$Test->expected_tests($max);

获取/设置我们期望此测试运行的测试数量,并打印出相应的标题。

no_plan
$Test->no_plan;

声明此测试将运行不确定的数量的测试。

done_testing
$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);
has_plan
$plan = $Test->has_plan

查找是否已定义计划。$plan 可以是 undef(未设置计划)、no_plan(测试数量不确定)或整数(预期测试数量)。

skip_all
$Test->skip_all;
$Test->skip_all($reason);

使用给定的 $reason 跳过所有测试。立即以 0 退出。

exported_to
my $pack = $Test->exported_to;
$Test->exported_to($pack);

告诉 Test::Builder 你将函数导出到哪个包。

此方法并不十分有用,因为共享相同 Test::Builder 对象的模块可能会导出到不同的包,并且只有最后一个包会被使用。

运行测试

这些实际上运行测试,类似于 Test::More 中的函数。

如果测试通过,它们都返回 true;如果测试失败,则返回 false。

$name 始终是可选的。

ok
$Test->ok($test, $name);

你的基本测试。如果 $test 为真,则通过;如果 $test 为假,则失败。就像 Test::Simple 的 ok() 一样。

is_eq
$Test->is_eq($got, $expected, $name);

就像 Test::More 的 is() 一样。检查 $got eq $expected 是否成立。这是字符串版本。

undef 只能匹配另一个 undef

is_num
$Test->is_num($got, $expected, $name);

就像 Test::More 的 is() 一样。检查 $got == $expected 是否成立。这是数字版本。

undef 只能匹配另一个 undef

isnt_eq
$Test->isnt_eq($got, $dont_expect, $name);

就像 Test::Moreisnt() 一样。检查 $got ne $dont_expect 是否成立。这是字符串版本。

isnt_num
$Test->isnt_num($got, $dont_expect, $name);

就像 Test::Moreisnt() 一样。检查 $got ne $dont_expect 是否成立。这是数字版本。

like
$Test->like($thing, qr/$regex/, $name);
$Test->like($thing, '/$regex/', $name);

就像 Test::Morelike() 一样。检查 $thing 是否匹配给定的 $regex

unlike
$Test->unlike($thing, qr/$regex/, $name);
$Test->unlike($thing, '/$regex/', $name);

就像 Test::Moreunlike() 一样。检查 $thing 是否**不匹配**给定的 $regex

cmp_ok
$Test->cmp_ok($thing, $type, $that, $name);

Test::Morecmp_ok() 功能相同。

$Test->cmp_ok($big_num, '!=', $other_big_num);

其他测试方法

这些是在编写测试过程中使用的方法,但本身并不是测试。

BAIL_OUT
$Test->BAIL_OUT($reason);

指示 Test::Harness 测试进行得非常糟糕,所有测试都应该终止。这包括运行任何其他测试脚本。

它将以 255 退出。

skip
$Test->skip;
$Test->skip($why);

跳过当前测试,报告 $why

todo_skip
$Test->todo_skip;
$Test->todo_skip($why);

skip() 相似,只是它会将测试声明为失败并标记为 TODO。类似于

print "not ok $tnum # TODO $why\n";

测试构建实用方法

这些方法在编写自己的测试方法时很有用。

maybe_regex
$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);
}
is_fh
my $is_fh = $Test->is_fh($thing);

确定给定的 $thing 是否可以用作文件句柄。

测试风格

level
$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 而不是将其设置为常量。

use_numbers
$Test->use_numbers($on_or_off);

测试是否应该输出数字。也就是说,如果为真

ok 1
ok 2
ok 3

或如果为假

ok
ok
ok

当您无法依赖测试输出顺序时,这非常有用,例如当涉及线程或分叉时。

默认为开启。

no_diag
$Test->no_diag($no_diag);

如果设置为真,则不会打印任何诊断信息。这包括对 diag() 的调用。

no_ending
$Test->no_ending($no_ending);

通常,Test::Builder 在测试结束时会进行一些额外的诊断。它还会更改退出代码,如下所述。

如果为真,则不会执行任何操作。

no_header
$Test->no_header($no_header);

如果设置为真,则不会打印“1..N”标题。

输出

控制测试输出的位置。

您的测试可以更改 STDOUT 和 STDERR 的指向位置,Test::Builder 的默认输出设置不会受到影响。

diag
$Test->diag(@msgs);

打印给定的 @msgs。类似于 print,参数只是简单地附加在一起。

通常,它使用 failure_output() 处理程序,但如果这是针对 TODO 测试,则使用 todo_output() 处理程序。

输出将缩进并用 # 标记,以避免干扰测试输出。如果末尾没有换行符,则会添加一个换行符。

我们鼓励使用此方法,而不是直接调用 print。

返回 false。为什么?因为 diag() 通常与失败的测试一起使用 (ok() || diag()),它会“传递”失败。

return ok(...) || diag(...);
note
$Test->note(@msgs);

类似于 diag(),但它打印到 output() 处理程序,因此用户通常不会看到它,除非处于详细模式。

explain
my @dump = $Test->explain(@msgs);

将以人类可读的格式转储任何引用的内容。对于以下情况非常有用...

is_deeply($have, $want) || diag explain $have;

is_deeply($have, $want) || note explain $have;
output
failure_output
todo_output
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。

reset_outputs
$tb->reset_outputs;

将所有输出文件句柄重置为默认值。

carp
$tb->carp(@message);

使用 @message 警告,但消息将显示为来自原始测试函数被调用的位置($tb->caller)。

croak
$tb->croak(@message);

使用 @message 退出,但消息将显示为来自原始测试函数被调用的位置($tb->caller)。

测试状态和信息

no_log_results

这将关闭结果的长期存储。调用此方法将使 detailssummary 无效。如果运行的测试数量足以填满所有可用内存,则可能需要使用此方法。

Test::Builder->new->no_log_results();

无法将其重新打开。

current_test
my $curr_test = $Test->current_test;
$Test->current_test($num);

获取/设置当前正在进行的测试编号。通常不需要设置此项。

如果向前设置,则缺少的测试的详细信息将被填充为“未知”。如果向后设置,则中间测试的详细信息将被删除。如果你真的想,可以擦除历史记录。

is_passing
my $ok = $builder->is_passing;

指示测试套件当前是否通过。

更正式地说,如果发生任何导致测试套件无法通过的情况,它将为假。否则为真。

例如,如果没有任何测试运行,is_passing() 将为真,因为即使没有测试的套件是失败的,你也可以向其中添加一个通过的测试并开始通过。

不要想太多。

summary
my @tests = $Test->summary;

到目前为止测试的简单摘要。通过为真,失败为假。这是一个逻辑上的通过/失败,所以待办事项是通过的。

当然,测试 #1 是 $tests[0],等等...

details
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'
  };
todo
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。

find_TODO
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);
in_todo
my $in_todo = $Test->in_todo;

如果测试当前在 TODO 块内,则返回 true。

todo_start
$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 方法调用情况下调用此方法,则该方法将致命。

caller
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::SimpleTest::MoreTest::ExceptionTest::Differences 都使用 Test::Builder。

另请参阅

内部

Test2Test2::API

遗留

Test::SimpleTest::More

外部

Test::Harness

作者

原始代码由 chromatic 编写,由 Michael G Schwern <[email protected]> 维护。

维护者

Chad Granum <[email protected]>

版权

版权所有 2002-2008 chromatic <[email protected]> 和 Michael G Schwern <[email protected]>。

本程序是自由软件;您可以根据与 Perl 本身相同的条款重新发布和/或修改它。

参见 https://perldotcom.perl5.cn/perl/misc/Artistic.html