ミューテーションテストの偽のkill:ベースライン・ファイルアクセス・kill理由
公開日 2026-10-27
ミューテーションテストは、コードを意図的に変更し(<を<=に、return xをreturn Noneに)、テストがそれに気づくかを確かめる手法です。「killed」と判定されたミュータントは、テストが失敗したことを意味します。しかし、テストはミューテーションとは無関係な理由でも失敗します。そうなると、ミューテーションスコアは実際のテストの実力より良く見えてしまいます。このガイドでは、こうした偽のkill(false kill)を見つける3つのチェックを紹介します。
偽のkillが起きる理由
| 原因 | 見えるもの |
|---|---|
| ミューテーションの前から、スイートが失敗していた、または不安定だった | 無関係な同じエラーによるkillが多数 |
| テストが変更後のコピーではなく、元のファイルをインポートしている | killがタイムアウトか環境エラーによるものだけ |
| 遅いマシンによるタイムアウト | 実行時間の長いテストにkillが集中 |
| 実行間で共有される状態(一時ファイル、キャッシュ、ポート) | 同じミュータントを再実行すると、killの結果が変わる |
タイムアウトはスコアに影響します。Strykerでは、detected = killed + timeout、mutation score = detected / valid × 100です。タイムアウトが多発するマシンでは、スコアが水増しされます。出典:Stryker mutant states and metrics、2026-10-19確認。
チェック1 — まっさらなコピーでベースラインを取る
- ミューテーションツールが使うのとまったく同じ条件(同じサンドボックス、同じ環境変数)で、プロジェクトを新しいディレクトリにコピーします。
- そこで、ミューテーションをかけずに、テストスイート全体を3回実行します。
- 3回とも通らなければなりません。ここでの失敗は既存の失敗で、後で偽のkillとして現れます。先に修正するか、隔離してください。
cp -r project /tmp/mt-baseline && cd /tmp/mt-baseline
for i in 1 2 3; do pytest -q -p no:cacheprovider || echo "BASELINE FAIL $i"; done
チェック2 — テストが変更後のファイルを読んだことを証明する
テストが変更後のファイルを一度も読み込まないなら、そのミュータントをkillできたはずがありません。ミュータントごとの実行中に、ファイルアクセスを記録します。Pythonなら、audit hookで十分です。
# conftest.py
import sys, os
TARGET = os.environ.get("MUT_TARGET") # path of the mutated file
def hook(event, args):
if event == "open" and TARGET and str(args[0]).endswith(TARGET):
open("/tmp/mut_touched", "a").write("1")
sys.addaudithook(hook)
各ミュータントの実行では、古いバイトコードを削除し(コピー内の__pycache__を消します)、ソースファイルが実際に読まれるようにします。ルールは次のとおりです。対象ファイルが一度も開かれなかったkillはsuspect(疑わしい)とマークして再実行します。他のスタックでは、strace -e trace=openat(Linux)やテストランナーのカバレッジデータが同じ役割を果たします。
チェック3 — killの理由の分布を見る
- killされたミュータントごとに、実行が失敗した理由を保存します。アサーションエラー、その他の例外、タイムアウト、クラッシュ、収集/インポートエラーです。
- 簡単な集計表を作ります。
| 理由 | 健全なパターン | 危険信号 |
|---|---|---|
| アサーション失敗 | 大半のkill | — |
| その他の例外 | いくらか | 多数のミュータントで同一のメッセージ |
| タイムアウト | 少数(無限ループのミュータント) | 割合が大きい。遅いマシンで増える |
| インポート/収集エラー | ほぼなし | 無視できない割合があれば、失敗しているのはテストではなくハーネス |
まとめ
- まっさらなコピーでのベースラインが3回中3回グリーン
- すべてのkillに「対象ファイルが開かれた」記録がある
- killの理由で最も多いのがアサーション失敗
- killの5%を無作為に選んで再実行し、結果が変わらない
FAQ
- タイムアウトを延ばすべきですか?
- ベースラインから決めてください。通常のスイートの実行時間を測り、その何倍かを許容します。短すぎると偽のkillが生まれ、長すぎると実行が遅くなります。
- コンパイルエラーはカウントされますか?
- Strykerでは、コンパイルエラーと実行時エラーは「invalid」なミュータントとなり、スコアには含まれません。
出典
- Stryker:ミュータントの状態と指標 — 確認日 2026-10-19
関連(韓国語): 開発知識まとめ(韓国語)(개발 지식 모음)
🌐 English · Español · Bahasa Indonesia · 日本語 · ไทย · Tiếng Việt · 中文