Movatterモバイル変換


[0]ホーム

URL:


Blog::kobaken

prove t/foo/bar/baz.t

subtest の名前でテスト対象をフィルタリングするTest2::Plugin::SubtestFilterをリリースしました

jest、vitestなどの--testNamePatternのようにPerlのテストで、subtestの名前でテスト対象をフィルタリングできるTest2::Plugin::SubtestFilterをリリースしました。特定のテストだけ実行したい時、便利だと思います!

# t/test.tuseTest2::V0;useTest2::Plugin::SubtestFilter;subtest'foo' =>sub{    subtest'nested foo1' =>sub{ ok1 };    subtest'nested foo2' =>sub{ ok1 };};subtest'baz' =>sub{    ok1;};done_testing;
# foo1 にマッチするテストだけ実行されている様子❯ SUBTEST_FILTER=foo1 prove -lvr t/test.tt/test.t ..# Seeded srand with seed '20251020' from local date.ok 1 - foo {    ok 1 - nested foo1 {        ok 1        1..1    }    ok 2 - nested foo2 # skip    1..2}ok 2 - baz # skip1..2okAll tests successful.Files=1, Tests=2,  0 wallclock secs ( 0.00 usr  0.00 sys +  0.04 cusr  0.00 csys =  0.04 CPU)Result: PASS

仕組み解説

1. subtest 関数のオーバーライド

プラグインは Test2 の subtest 関数を動的に置き換えます:

# lib/Test2/Plugin/SubtestFilter.pm:14-26subimport{my$class =shift;my$caller =caller;# 呼び出し元の名前空間から元の subtest 関数を取得my$orig =$caller->can('subtest')orreturn;# subtest をフィルタリング機能付きのものに置き換えno strict'refs';no warnings'redefine';*{"${caller}::subtest"} = _create_filtered_subtest($orig,$caller);}

2. Test2API によるサブテスト名の追跡

Test2 の context と hub を使って、ネストされたサブテストの階層を追跡します:

# lib/Test2/Plugin/SubtestFilter.pm:67-72my$ctx = context();my$hub =$ctx->hub;# 現在のサブテスト名をメタデータに保存$hub->set_meta(subtest_name =>$name);# スタック上のすべてのサブテスト名を取得my@stacked_subtest_names =map{$_->get_meta('subtest_name')}$ctx->stack->all;# 完全なサブテスト名を構築(例: "parent child grandchild")my$current_subtest_fullname =join$SEPARATOR,@stacked_subtest_names;

3. B::Deparse による静的コード解析

子サブテストの存在を事前に検出するために、B::Deparse を使用してコードを文字列に変換し、正規表現で解析します:

# lib/Test2/Plugin/SubtestFilter.pm:82-92my$obj    = B::svref_2object(\$code);my$source =$deparse->coderef2text($code);# サブテストの呼び出しをパターンマッチで抽出my@child_subtest_names =$source =~/subtest\(['"](.+?)['"]/g;# UTF-8デコード処理(\x{XXXX} 形式を文字に変換)if (@child_subtest_names) {my@child_subtest_fullnames =map{my$decoded =$_;$decoded =~s/\\x\{([0-9a-fA-F]+)\}/chr(hex($1))/ge;join$SEPARATOR,$current_subtest_fullname,$decoded;}@child_subtest_names;    ...}

4. フィルタリングのロジック

3段階のチェックでフィルタリングを実行します:

# ステップ1: 現在のサブテスト名がフィルタにマッチ?if ($current_subtest_fullname =~$filter) {# マッチした場合、このサブテストとすべての子を実行my$pass =$original_subtest->($name,$params,$code,@args);$ctx->release;return$pass;}# ステップ2: 子サブテストのいずれかがマッチ?if (@child_subtest_names) {my@child_subtest_fullnames =map{ ...}@child_subtest_names;if (any {$_ =~$filter }@child_subtest_fullnames) {# 子がマッチした場合、親を実行(子のフィルタリングは再帰的に行われる)my$pass =$original_subtest->($name,$params,$code,@args);$ctx->release;return$pass;    }}# ステップ3: マッチしない場合はスキップ$ctx->skip($name);$ctx->release;return1;

以上です!

検索

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp