內容

名稱

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)

這會拋出一個例外,報告內容的檔案和行號。這也會為你釋放內容。

$ctx->alert($message)

這會從內容的檔案和行號發出警告。

$stack = $ctx->stack()

這會回傳 Test2::API::Stack 執行個體,內容用來尋找目前的集線器。

$hub = $ctx->hub()

這會回傳 Test2::Hub 執行個體,內容辨識為目前一個,所有事件都應傳送給它。

$dbg = $ctx->trace()

這會回傳 Test2::EventFacet::Trace 執行個體,內容使用它。

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

有時你有一個不是目前的內容,而且你想要使用它作為目前的內容。在這些情況下,你可以呼叫 $ctx->do_in_context(sub { ... })。程式區塊將會執行,而且在其中尋找內容的任何東西都會找到呼叫該方法的內容。

不會影響其他集線器上的內容,只有內容使用的集線器會受到影響。

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()

當建立內容時 $@ 的值。

EVENT PRODUCTION METHODS

我該使用哪一個?

如果 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)

這讓您可以直接從 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 種鉤子,初始化鉤子和釋放鉤子。顧名思義,這些鉤子會在建立或釋放內容時觸發。

初始化鉤子

這些鉤子會在每次初始化內容時呼叫。這表示在建立新執行個體時。這些鉤子不會在每次有東西要求內容時呼叫,只會在建立新內容時呼叫。

全域

以下是新增全域初始化回呼的方式。全域回呼會發生在任何 hub 或堆疊的每個內容。

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

每個 hub

以下是為給定 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 中執行。

$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/

維護人員

Chad Granum <exodist@cpan.org>

作者

Chad Granum <exodist@cpan.org>
Kent Fredric <kentnl@cpan.org>

著作權

著作權所有 2020 Chad Granum <exodist@cpan.org>。

本程式為自由軟體;您可以在與 Perl 相同的條款下重新散布或修改它。

請參閱 http://dev.perl.org/licenses/