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プラグインは 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);}
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;
子サブテストの存在を事前に検出するために、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; ...}
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;
以上です!
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。