内容

名称

Test2::API::Context - 表示测试上下文的对象。

说明

上下文对象是使用 Test2 编写测试工具的作者的主要接口。上下文对象表示测试发生的环境(文件和行号),并提供了一种从该环境生成事件的快速方法。上下文对象还负责将事件发送到正确的 Test2::Hub 实例。

概要

通常,您不会直接创建上下文。要获取上下文,您应该始终使用 Test2::API 模块导出的 context()

use Test2::API qw/context/;

sub my_ok {
    my ($bool, $name) = @_;
    my $ctx = context();

    if ($bool) {
        $ctx->pass($name);
    }
    else {
        $ctx->fail($name);
    }

    $ctx->release; # You MUST do this!
    return $bool;
}

上下文对象可以轻松包装也使用上下文的其他工具。一旦您获取了一个上下文,在释放上下文之前您调用的任何工具都将继承它

sub wrapper {
    my ($bool, $name) = @_;
    my $ctx = context();
    $ctx->diag("wrapping my_ok");

    my $out = my_ok($bool, $name);
    $ctx->release; # You MUST do this!
    return $out;
}

关键细节

您必须始终使用 Test2::API 中的 context() 子例程

通过 Test2::API::Context->new() 创建您自己的上下文几乎永远不会产生理想的结果。使用 Test2::API 导出的 context()

在少数情况下,工具作者可能希望手动创建一个新上下文,这就是 new 方法存在的原因。除非你真的知道自己在做什么,否则你应该避免这样做。

完成时,你必须始终释放上下文

释放上下文会告诉系统你已完成。这给了它一个机会来运行任何必要的回调或清理任务。如果你忘记释放上下文,它将尝试检测问题并向你发出警告。

你不得传递上下文对象

当你获取一个上下文对象时,它专门针对你的工具和任何嵌套在内的工具而创建。如果你传递一个上下文,你就有可能用不正确的上下文信息污染其他工具。

如果你确定要让不同的工具使用相同的上下文,你可以传递一个快照。$ctx->snapshot 将为你提供一个上下文的浅层克隆,可以安全地传递或存储。

你不得存储或缓存上下文以备后用

只要给定集线器存在上下文,所有尝试获取上下文的工具都将获取现有实例。如果你尝试存储上下文,你将用不正确的上下文信息污染其他工具。

如果你确定要将上下文保存以备后用,你可以使用快照。$ctx->snapshot 将为你提供一个上下文的浅层克隆,可以安全地传递或存储。

如果你确实导致上下文在获取它的范围之外持续存在,context() 有一些机制来保护你。在实践中,你不应该依赖这些保护措施,而且它们会发出相当嘈杂的警告。

你应该在给定的工具中尽快获取你的上下文

你永远不知道你从自己的工具中调用的哪些工具需要上下文。尽早获取上下文可确保嵌套工具可以找到你希望它们找到的上下文。

方法

$ctx->done_testing;

请注意,测试已完成。如果尚未设置计划,这将生成计划事件。

$clone = $ctx->snapshot()

这将返回上下文的浅层克隆。浅层克隆可以安全地存储以备后用。

$ctx->release()

这将释放上下文。这将运行清理任务和几个重要的钩子。它还将还原 $!$?$@,使其恢复到创建上下文时的状态。

注意:如果多次获取上下文,则会保留内部引用计数。release() 会递减引用计数,除非引用计数达到 0,否则 release() 的其他操作都不会发生。这意味着只有对 release() 的最后一次调用才会重置 $?$!$@,并运行清理任务。

$ctx->throw($message)

这将抛出一个异常,报告到上下文的 file 和 line number。这还将为您释放上下文。

$ctx->alert($message)

这将从上下文的 file 和 line number 发出警告。

$stack = $ctx->stack()

这将返回 Test2::API::Stack 实例,该实例由上下文用于查找当前 hub。

$hub = $ctx->hub()

这将返回 Test2::Hub 实例,该实例由上下文识别为当前实例,所有事件都应发送到该实例。

$dbg = $ctx->trace()

这将返回上下文使用的 Test2::EventFacet::Trace 实例。

$ctx->do_in_context(\&code, @args);

有时您有一个不是当前的上下文,并且您希望将其用作当前上下文。在这些情况下,您可以调用 $ctx->do_in_context(sub { ... })。将运行代码块,并且其中查找上下文的任何内容都将找到调用该方法的上下文。

不会影响其他 hub 上的上下文,只有上下文使用的 hub 会受到影响。

my $ctx = ...;
$ctx->do_in_context(sub {
    my $ctx = context(); # returns the $ctx the sub is called on
});

注意:上下文实际上会被克隆,克隆将被用作原始文件。这允许线程 ID、进程 ID 和错误变量在不修改原始上下文的情况下保持正确。

$ctx->restore_error_vars()

这会将 $!$?$@ 设置为在创建上下文时的值。这里没有本地化或任何操作,调用此方法实际上会设置这些变量。

$! = $ctx->errno()

创建上下文时 $! 的(数字)值。

$? = $ctx->child_error()

创建上下文时 $? 的值。

$@ = $ctx->eval_error()

创建上下文时 $@ 的值。

事件生成方法

我该使用哪一个?

如果 pass*fail* 满足你的情况,它们是最佳选择,使用其中之一始终是最优的。也就是说,它们通过消除许多功能来实现优化。

诸如 oknote 之类的方法是基于旧 API 生成常见 1 任务事件的快捷方式,但是它们具有前向兼容性,并且易于使用。如果这些满足你的需求,那么继续使用它们,但请经常回来查看可能添加的替代方法。

如果你想生成新样式事件,一次执行多项操作的事件,那么你想要 *ev2* 方法。这些方法允许你直接指定希望使用的方面。

$event = $ctx->pass()
$event = $ctx->pass($name)

这将发送并返回一个 Test2::Event::Pass 事件。你可以选择为断言提供一个 $name

Test2::Event::Pass 是一个专门设计和优化的事件,使用它将有助于通过测试的性能。

$true = $ctx->pass_and_release()
$true = $ctx->pass_and_release($name)

这是 pass()release() 的组合。如果您不打算在发送事件后对上下文执行任何操作,则可以使用此方法。这有助于编写更清晰、更简洁的代码。

sub shorthand {
    my ($bool, $name) = @_;
    my $ctx = context();
    return $ctx->pass_and_release($name) if $bool;

    ... Handle a failure ...
}

sub longform {
    my ($bool, $name) = @_;
    my $ctx = context();

    if ($bool) {
        $ctx->pass($name);
        $ctx->release;
        return 1;
    }

    ... Handle a failure ...
}
my $event = $ctx->fail()
my $event = $ctx->fail($name)
my $event = $ctx->fail($name, @diagnostics)

这允许您发送 Test2::Event::Fail 事件。您可以选择提供 $name@diagnostics 消息。

诊断消息可以是简单的字符串、数据结构或 Test2::EventFacet::Info::Table 实例(会内联转换为 Test2::EventFacet::Info 结构)。

my $false = $ctx->fail_and_release()
my $false = $ctx->fail_and_release($name)
my $false = $ctx->fail_and_release($name, @diagnostics)

这是 fail()release() 的组合。这可用于编写更清晰、更简洁的代码。

sub shorthand {
    my ($bool, $name) = @_;
    my $ctx = context();
    return $ctx->fail_and_release($name) unless $bool;

    ... Handle a success ...
}

sub longform {
    my ($bool, $name) = @_;
    my $ctx = context();

    unless ($bool) {
        $ctx->pass($name);
        $ctx->release;
        return 1;
    }

    ... Handle a success ...
}
$event = $ctx->ok($bool, $name)
$event = $ctx->ok($bool, $name, \@on_fail)

注意:不建议使用此方法,而建议使用 pass()fail(),它们会生成 Test2::Event::PassTest2::Event::Fail 事件。这些较新的事件类型速度更快,而且不那么繁琐。

这将为您创建一个 Test2::Event::Ok 对象。如果 $bool 为 false,那么还会发送一个 Test2::Event::Diag 事件,其中包含有关失败的详细信息。如果您不想要自动诊断,则应直接使用 send_event() 方法。

第三个参数 \@on_fail) 是在测试失败时发送的一组可选诊断。与 fail() 不同,这些诊断必须是纯字符串,不支持数据结构。

$event = $ctx->note($message)

发送 Test2::Event::Note。此事件会将消息打印到 STDOUT。

$event = $ctx->diag($message)

发送 Test2::Event::Diag。此事件会将消息打印到 STDERR。

$event = $ctx->plan($max)
$event = $ctx->plan(0, 'SKIP', $reason)

这可用于发送 Test2::Event::Plan 事件。此事件通常采用您期望运行的测试数量。或者,您可以将预期计数设置为 0,并给出带有原因的 'SKIP' 指令,以导致跳过所有测试。

$event = $ctx->skip($name, $reason);

发送 Test2::Event::Skip 事件。

$event = $ctx->bail($reason)

这会发送 Test2::Event::Bail 事件。此事件将完全终止所有测试。

$event = $ctx->send_ev2(%facets)

这允许您直接从切面构建和发送 V2 事件。该事件在发送后返回。

此示例发送单个断言、注释(Test::Builder 中的 stdout 注释)并设置计划为 1。

my $event = $ctx->send_event(
    plan   => {count => 1},
    assert => {pass  => 1, details => "A passing assert"},
    info => [{tag => 'NOTE', details => "This is a note"}],
);
$event = $ctx->build_e2(%facets)

这与 send_ev2() 相同,只是它构建并返回事件而不发送它。

$event = $ctx->send_ev2_and_release($Type, %parameters)

这是 send_ev2()release() 的组合。

sub shorthand {
    my $ctx = context();
    return $ctx->send_ev2_and_release(assert => {pass => 1, details => 'foo'});
}

sub longform {
    my $ctx = context();
    my $event = $ctx->send_ev2(assert => {pass => 1, details => 'foo'});
    $ctx->release;
    return $event;
}
$event = $ctx->send_event($Type, %parameters)

最好在新的代码中使用 send_ev2()。

这允许您构建和发送任何类型的事件。$Type 参数应该是去掉 Test2::Event:: 的事件包名称,或者以 '+' 为前缀的完全限定包名称。该事件在发送后返回。

my $event = $ctx->send_event('Ok', ...);

my $event = $ctx->send_event('+Test2::Event::Ok', ...);
$event = $ctx->build_event($Type, %parameters)

最好在新的代码中使用 build_ev2()。

这与 send_event() 相同,只是它构建并返回事件而不发送它。

$event = $ctx->send_event_and_release($Type, %parameters)

最好在新的代码中使用 send_ev2_and_release()。

这是 send_event()release() 的组合。

sub shorthand {
    my $ctx = context();
    return $ctx->send_event_and_release(Pass => { name => 'foo' });
}

sub longform {
    my $ctx = context();
    my $event = $ctx->send_event(Pass => { name => 'foo' });
    $ctx->release;
    return $event;
}

HOOKS

有 2 种类型的钩子,init 钩子和 release 钩子。顾名思义,这些钩子在创建或释放上下文时触发。

INIT HOOKS

这些会在上下文初始化时调用。这意味着在创建新实例时。这些钩子不会在每次请求上下文时调用,仅在创建新上下文时调用。

GLOBAL

这是添加全局初始化回调的方式。全局回调会对任何 hub 或栈的每个上下文发生。

Test2::API::test2_add_callback_context_init(sub {
    my $ctx = shift;
    ...
});

PER HUB

这是为给定 hub 创建的所有上下文添加初始化回调的方式。这些回调不会对其他 hub 运行。

$hub->add_context_init(sub {
    my $ctx = shift;
    ...
});

PER CONTEXT

这是指定初始化钩子(仅在对 context() 的调用生成新上下文时运行)的方式。如果 context() 返回现有上下文,则回调将被忽略。

my $ctx = context(on_init => sub {
    my $ctx = shift;
    ...
});

RELEASE HOOKS

这些会在上下文释放时调用。这意味着在对实例的最后一个引用即将销毁时。这些钩子不会在每次调用 $ctx->release 时调用。

GLOBAL

这是添加全局释放回调的方式。全局回调会对任何 hub 或栈的每个上下文发生。

Test2::API::test2_add_callback_context_release(sub {
    my $ctx = shift;
    ...
});

PER HUB

这是为给定 hub 创建的所有上下文添加释放回调的方式。这些回调不会对其他 hub 运行。

$hub->add_context_release(sub {
    my $ctx = shift;
    ...
});

PER CONTEXT

这是将释放回调直接添加到上下文的方式。回调始终会被添加到返回的上下文,无论生成新上下文还是返回现有上下文。

my $ctx = context(on_release => sub {
    my $ctx = shift;
    ...
});

THIRD PARTY META-DATA

此对象使用 Test2::Util::ExternalMeta,它提供了一种一致的方式,用于将元数据附加到此类的实例。这对于工具、插件和其他扩展很有用。

SOURCE

Test2 的源代码存储库可在 http://github.com/Test-More/test-more/ 中找到。

MAINTAINERS

Chad Granum <[email protected]>

作者

Chad Granum <[email protected]>
Kent Fredric <[email protected]>

版权

版权所有 2020 Chad Granum <[email protected]>。

此程序是免费软件;您可以在与 Perl 本身相同的条款下重新分发和/或修改它。

请参阅 https://dev.perl5.cn/licenses/