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->new()
创建您自己的上下文几乎永远不会产生理想的结果。使用 Test2::API 导出的 context()
。
在少数情况下,工具作者可能希望手动创建一个新上下文,这就是 new
方法存在的原因。除非你真的知道自己在做什么,否则你应该避免这样做。
释放上下文会告诉系统你已完成。这给了它一个机会来运行任何必要的回调或清理任务。如果你忘记释放上下文,它将尝试检测问题并向你发出警告。
当你获取一个上下文对象时,它专门针对你的工具和任何嵌套在内的工具而创建。如果你传递一个上下文,你就有可能用不正确的上下文信息污染其他工具。
如果你确定要让不同的工具使用相同的上下文,你可以传递一个快照。$ctx->snapshot
将为你提供一个上下文的浅层克隆,可以安全地传递或存储。
只要给定集线器存在上下文,所有尝试获取上下文的工具都将获取现有实例。如果你尝试存储上下文,你将用不正确的上下文信息污染其他工具。
如果你确定要将上下文保存以备后用,你可以使用快照。$ctx->snapshot
将为你提供一个上下文的浅层克隆,可以安全地传递或存储。
如果你确实导致上下文在获取它的范围之外持续存在,context()
有一些机制来保护你。在实践中,你不应该依赖这些保护措施,而且它们会发出相当嘈杂的警告。
你永远不知道你从自己的工具中调用的哪些工具需要上下文。尽早获取上下文可确保嵌套工具可以找到你希望它们找到的上下文。
请注意,测试已完成。如果尚未设置计划,这将生成计划事件。
这将返回上下文的浅层克隆。浅层克隆可以安全地存储以备后用。
这将释放上下文。这将运行清理任务和几个重要的钩子。它还将还原 $!
、$?
和 $@
,使其恢复到创建上下文时的状态。
注意:如果多次获取上下文,则会保留内部引用计数。release()
会递减引用计数,除非引用计数达到 0,否则 release()
的其他操作都不会发生。这意味着只有对 release()
的最后一次调用才会重置 $?
、$!
、$@
,并运行清理任务。
这将抛出一个异常,报告到上下文的 file 和 line number。这还将为您释放上下文。
这将从上下文的 file 和 line number 发出警告。
这将返回 Test2::API::Stack 实例,该实例由上下文用于查找当前 hub。
这将返回 Test2::Hub 实例,该实例由上下文识别为当前实例,所有事件都应发送到该实例。
这将返回上下文使用的 Test2::EventFacet::Trace 实例。
有时您有一个不是当前的上下文,并且您希望将其用作当前上下文。在这些情况下,您可以调用 $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 和错误变量在不修改原始上下文的情况下保持正确。
这会将 $!
、$?
和 $@
设置为在创建上下文时的值。这里没有本地化或任何操作,调用此方法实际上会设置这些变量。
创建上下文时 $!
的(数字)值。
创建上下文时 $?
的值。
创建上下文时 $@
的值。
我该使用哪一个?
如果 pass*
和 fail*
满足你的情况,它们是最佳选择,使用其中之一始终是最优的。也就是说,它们通过消除许多功能来实现优化。
诸如 ok
和 note
之类的方法是基于旧 API 生成常见 1 任务事件的快捷方式,但是它们具有前向兼容性,并且易于使用。如果这些满足你的需求,那么继续使用它们,但请经常回来查看可能添加的替代方法。
如果你想生成新样式事件,一次执行多项操作的事件,那么你想要 *ev2*
方法。这些方法允许你直接指定希望使用的方面。
这将发送并返回一个 Test2::Event::Pass 事件。你可以选择为断言提供一个 $name
。
Test2::Event::Pass 是一个专门设计和优化的事件,使用它将有助于通过测试的性能。
这是 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 ...
}
这允许您发送 Test2::Event::Fail 事件。您可以选择提供 $name
和 @diagnostics
消息。
诊断消息可以是简单的字符串、数据结构或 Test2::EventFacet::Info::Table 实例(会内联转换为 Test2::EventFacet::Info 结构)。
这是 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 ...
}
注意:不建议使用此方法,而建议使用 pass()
和 fail()
,它们会生成 Test2::Event::Pass 和 Test2::Event::Fail 事件。这些较新的事件类型速度更快,而且不那么繁琐。
这将为您创建一个 Test2::Event::Ok 对象。如果 $bool
为 false,那么还会发送一个 Test2::Event::Diag 事件,其中包含有关失败的详细信息。如果您不想要自动诊断,则应直接使用 send_event()
方法。
第三个参数 \@on_fail
) 是在测试失败时发送的一组可选诊断。与 fail()
不同,这些诊断必须是纯字符串,不支持数据结构。
发送 Test2::Event::Note。此事件会将消息打印到 STDOUT。
发送 Test2::Event::Diag。此事件会将消息打印到 STDERR。
这可用于发送 Test2::Event::Plan 事件。此事件通常采用您期望运行的测试数量。或者,您可以将预期计数设置为 0,并给出带有原因的 'SKIP' 指令,以导致跳过所有测试。
发送 Test2::Event::Skip 事件。
这会发送 Test2::Event::Bail 事件。此事件将完全终止所有测试。
这允许您直接从切面构建和发送 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"}],
);
这与 send_ev2()
相同,只是它构建并返回事件而不发送它。
这是 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;
}
最好在新的代码中使用 send_ev2()。
这允许您构建和发送任何类型的事件。$Type
参数应该是去掉 Test2::Event::
的事件包名称,或者以 '+' 为前缀的完全限定包名称。该事件在发送后返回。
my $event = $ctx->send_event('Ok', ...);
或
my $event = $ctx->send_event('+Test2::Event::Ok', ...);
最好在新的代码中使用 build_ev2()。
这与 send_event()
相同,只是它构建并返回事件而不发送它。
最好在新的代码中使用 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;
}
有 2 种类型的钩子,init 钩子和 release 钩子。顾名思义,这些钩子在创建或释放上下文时触发。
这些会在上下文初始化时调用。这意味着在创建新实例时。这些钩子不会在每次请求上下文时调用,仅在创建新上下文时调用。
这是添加全局初始化回调的方式。全局回调会对任何 hub 或栈的每个上下文发生。
Test2::API::test2_add_callback_context_init(sub {
my $ctx = shift;
...
});
这是为给定 hub 创建的所有上下文添加初始化回调的方式。这些回调不会对其他 hub 运行。
$hub->add_context_init(sub {
my $ctx = shift;
...
});
这是指定初始化钩子(仅在对 context()
的调用生成新上下文时运行)的方式。如果 context()
返回现有上下文,则回调将被忽略。
my $ctx = context(on_init => sub {
my $ctx = shift;
...
});
这些会在上下文释放时调用。这意味着在对实例的最后一个引用即将销毁时。这些钩子不会在每次调用 $ctx->release
时调用。
这是添加全局释放回调的方式。全局回调会对任何 hub 或栈的每个上下文发生。
Test2::API::test2_add_callback_context_release(sub {
my $ctx = shift;
...
});
这是为给定 hub 创建的所有上下文添加释放回调的方式。这些回调不会对其他 hub 运行。
$hub->add_context_release(sub {
my $ctx = shift;
...
});
这是将释放回调直接添加到上下文的方式。回调始终会被添加到返回的上下文,无论生成新上下文还是返回现有上下文。
my $ctx = context(on_release => sub {
my $ctx = shift;
...
});
此对象使用 Test2::Util::ExternalMeta,它提供了一种一致的方式,用于将元数据附加到此类的实例。这对于工具、插件和其他扩展很有用。
Test2 的源代码存储库可在 http://github.com/Test-More/test-more/ 中找到。
版权所有 2020 Chad Granum <[email protected]>。
此程序是免费软件;您可以在与 Perl 本身相同的条款下重新分发和/或修改它。
请参阅 https://dev.perl5.cn/licenses/