mutation-testing について
mutation-testingは、変異テストの実行をAgentに案内するスキルです。PR作成前にテスト品質を確認するチーム向けで、プロジェクトに応じたツールを選び、変異の生存状況からテストの不足を調べます。
主な機能
- 01プロジェクトの識別情報に応じたツール選択
- 02関連コードに絞ったテスト実行
- 03変異数・生存数・スコアの報告
- 04シナリオに基づく生存変異の確認
- 05100%未満の場合の標準PRフロー停止
活用シーン
- 01PR作成前にテストの欠陥検出力を確認
- 02テストで検出されないコード変更の特定
- 03生存変異をシナリオに結び付けてテストを提案
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jwilger/ agent-skills --skill mutation-testingインストール後、Claude Code で/mutation-testingを入力して読み込まれたか確認します。
詳細
ミューテーションテスト
価値: フィードバック — ミューテーションテストは、テストが防ぐとされるバグを実際に検出できることを証明し、検証のループを完結させます。これがなければ、テストが成功しても誤った安心感を与える可能性があります。
目的
PR の作成前に、品質ゲートとしてミューテーションテストを実行するようエージェントに教えます。ミューテーションテストでは、本番コードに小さな変更(ミューテーション)を加え、テストがそれを検出できるかどうかを確認します。ミューテーションが生き残った場合、バグが検出されずに隠れている可能性のあるギャップが明らかになります。必要なミューテーションキル率は 100% です。
プラクティス
適切なツールを検出して実行する
プロジェクトの種類を検出し、適切なミューテーションテストツールを実行します。
-
プロジェクトの目印を確認します。
Cargo.toml→ Rust →cargo mutantspackage.json→ TypeScript/JavaScript →npx stryker runpyproject.tomlまたはsetup.py→ Python →mutmut runmix.exs→ Elixir →mix muzak
-
ツールがインストールされていることを確認します。インストールされていない場合は、インストール手順を提示します。
- Rust:
cargo install cargo-mutants - TypeScript:
npm install --save-dev @stryker-mutator/core - Python:
pip install mutmut - Elixir:deps に
{:muzak, "~> 1.0", only: :test}を追加します
- Rust:
-
対象範囲を適切に絞ってミューテーションテストを実行します。可能であれば、コードベース全体ではなく変更したファイルやパッケージを対象にします。
# Rust (scoped to package) cargo mutants --package <package> --jobs 4 # TypeScript npx stryker run # Python (scoped to source) mutmut run --paths-to-mutate=src/ mutmut results # Elixir mix muzak
結果を解析して報告する
ミューテーションテストツールの出力から、次の項目を抽出します。
- 生成されたミューテーションの総数
- キルされたミューテーション(テストが変更を検出したもの)
- 生き残ったミューテーション(テストが変更を検出しなかったもの)
- タイムアウトしたミューテーション
- ミューテーションスコアの割合
生き残ったミューテーションを分析する
生き残ったミューテーションごとに、次の 3 つを報告します。
- 場所: ファイルと行番号
- ミューテーション: 変更内容(例:「
+を-に置き換えた」) - 意味: どの種類のバグが見逃される可能性があるか
一般的なミューテーションの種類と、生き残った場合に示されること:
- 算術(
+→-、*→/):計算が検証されていない - 比較(
>→>=、==→!=):境界条件がテストされていない - ブール値(
&&→||、!を削除):ロジック分岐が網羅されていない - 戻り値(
true→false、Ok→Err):戻り値の経路が確認されていない - ステートメントの削除(行を削除):副作用がアサートされていない
シナリオのカバレッジ確認
生き残ったミューテーションに対するテストを推奨する前に、シナリオのカバレッジを確認します。
生き残ったミューテーションごとに、次の手順を実施します。
ステップ 1 — シナリオ確認: 受け入れシナリオまたはドメインシナリオ(スライスの acceptance_scenarios 配列または domain_scenarios 配列、あるいは計画の「確認済みシナリオ」セクション)に、ミューテーション対象の動作が要求されていますか?
- はい → シナリオは存在しますが、そのテストがありません。以下の「不足しているテストを提案する」に進みます。
- いいえ → 人間の判断が必要であることを示します。
カバレッジのないミューテーションについて、テストの提案に進んではいけません。シナリオなしでテストを書くことは、実際の動作をテストせずに指標を操作することになります。Surviving mutant at [file]:[line] has no GWT scenario requiring this behavior. Options: (a) Delete the code — this behavior may not be needed. (b) Add a missing acceptance or domain scenario — the spec is incomplete. The 100% kill rate still applies; this clarifies how to resolve it.
不足しているテストを提案する
対応するシナリオがある生き残ったミューテーションごとに、具体的なテストを提案します。
Surviving: src/money.rs:45 -- replaced `+` with `-` in Money::add()
Recommend: Test that adding Money(50) + Money(30) equals Money(80),
not Money(20). The current tests do not assert the sum value.
Surviving: src/account.rs:78 -- replaced `>` with `>=` in check_balance()
Recommend: Test the exact boundary -- check_balance with exactly zero
balance. Current tests only check positive and negative.
構造化出力
ミューテーションテストの完了後、MUTATION_RESULT 証跡パケットを生成します。
{
"tool": "cargo-mutants",
"scope": ["src/money.rs", "src/account.rs"],
"total_mutants": 42,
"killed": 40,
"survived": 2,
"score": 95.2,
"survivors": [
{"file": "src/money.rs", "line": 45, "mutation_type": "arithmetic", "description": "replaced + with -"}
],
"verdict": "FAIL"
}
- 判定: 変更したファイルのスコアが 100% の場合は
PASS、それ以外はFAIL - パイプラインモードで実行する場合は、
.factory/audit-trail/slices/<slice-id>/mutation.jsonに保存します - スタンドアロンで実行する場合、出力は情報提供のみです。表示した後、品質ゲートに進みます
品質ゲートを適用する
必要なミューテーションキル率は 100% です。すべてのミュータントをキルする必要があります。
- スコアが100%の場合:成功を報告し、PR の作成に進みます
- スコアが100%未満の場合:生存しているミュータントをすべて推奨事項とともに列挙します。PR の作成を明確な警告とともにブロックします。ユーザーは上書きできますが、デフォルトでは先に修正します。
実施すること:
- 可能であれば、変更したコードに対象を絞ってミューテーションテストを実行します
- 生存しているミュータントを、実行可能な修正の推奨事項とともに報告します
- 修正後に再実行し、すべてのミュータントがキルされたことを確認します
- タイムアウトはキルされたものとして扱います(ミューテーションによって何かが壊れています)
実施しないこと:
- PR 作成前のミューテーションテストを省略すること
- 生存しているミュータントを報告せずに受け入れること
- 変更したのがモジュールだけなのに、コードベース全体に対してミューテーションテストを実行すること
- ドメイン型で処理すべきデータ検証に対するテストを推奨すること
パイプラインモード
パイプラインオーケストレーターから呼び出された場合:
FAIL判定は、生存しているミュータントのリストを添えて自動的にtddスキルへ戻されます。パイプラインがこの手戻りのルーティングを処理するため、mutation-testing は結果を報告するだけです。MUTATION_RESULTパケット内の生存ミュータントの詳細は、ミューテーションツールを再実行して失敗内容を把握しなくても TDD ペアが対象を絞ったテストを書けるよう、ファイル、行、ミューテーションの種類、説明を具体的に記載する必要があります。- パイプラインはスライスごとに mutation-testing を複数回実行する場合があります。各実行では、そのスライスの前回の
mutation.jsonが上書きされます。
適用に関する注記
- パイプラインモード:ゲートとして適用します。キル率100%がゲートとなり、満たさない場合はマージがブロックされます。
- スタンドアロンモード:助言として扱います。エージェントは報告しますが、上書きを阻止することはできません。
厳守事項:
- キル率100%:パイプラインモードでは
[H]、スタンドアロンでは[RP](PR をブロックし、ユーザーは理由を文書化すれば上書き可能)
上書きに必要な文書化の要件については、テンプレートディレクトリにある CONSTRAINT-RESOLUTION.md を参照してください。
制約事項
- キル率100%:100%は100%です。「十分近い」ではありません。「理由があるから98%」でもありません。100%未満で許される唯一の方法は、ユーザーが明示的に上書きすることであり、その場合は、生存しているミュータントそれぞれが許容できる理由を必ず文書化する必要があります。「リリースしたい」は理由になりません。「このミュータントはビジネスルールではないログ出力を検証する」は理由になります。
- 「シナリオのないテストは禁止」:対応するシナリオなしに、ミュータントをキルすることだけを目的にテストを書くのは、指標の不正利用です。そのテストが証明するのは、ミュータントをキルできるということだけであり、その動作が重要であることではありません。その動作を対象とするシナリオがなければ、チームに不足を報告するのが正しい対応です。不足しているシナリオから、要件の欠落が明らかになる可能性もあります。
検証
ミューテーションテストの完了後、次の項目を確認してください。
- 関連する範囲を対象にミューテーションテストツールを実行した
- 生存しているミュータントをすべて、ファイル、行、ミューテーションの種類とともに列挙した
- テストの推奨事項を提示する前に、生存しているミュータントそれぞれについてシナリオの対象かどうかを確認した
- 対象となるシナリオがある各ミュータントについて、具体的なテストを推奨した
- 対象となるシナリオがない各ミュータントについて、人による判断が必要であると明示した
- ミューテーションスコアが100%である(またはユーザーが明示的に上書きを選択した)
- 修正を行った場合、確認のためにミューテーションテストを再実行した
いずれかの項目を満たしていない場合は、先に進む前に該当するプラクティスを再確認してください。
依存関係
このスキルは単独でも機能しますが、PR 前の品質ゲートとして最も効果を発揮します。 次のスキルと連携します。
- tdd: TDD ではミューテーションテストで検証するテストを作成します。生存しているミュータントは、TDD サイクルでケースを見落としたことを示します
- code-review: ミューテーションテストの結果はコードレビューに役立ちます。レビュアーは、新しいコードに生存しているミュータントがないことを確認できます
依存関係が見つからない場合は、次を使ってインストールしてください:
npx skills add jwilger/agent-skills --skill tdd