ミューテーションテストとは?
ミューテーションテストは、意図的に小さなバグを注入し、テストがそれを検出するかを確認することで、テストの品質を測定します。
カバレッジはテスト中にどのコードが実行されたかを教えてくれますが、テストがバグに気づくかどうかは教えてくれません。ミューテーションテストはその難しい問いに答えます。コードをミューテート(演算子を反転させたり定数を変更したり)し、スイートを再実行します。テストが失敗すればそのミュータントは「殺され」、すべてのテストが依然としてパスすればミュータントは「生き残り」、弱点を露呈します。
ミュータントの作り方
ミューテーションツールは、それぞれ1つの小さな変更を加えたコードのわずかに変更されたバージョンを多数生成します。プラスがマイナスになり、より小さいがより小さいか等しいになり、戻り値が削除されるといった具合です。各ミュータントは、開発者が導入しうる現実的なバグを表します。
殺されたか生き残ったか
各ミュータントについてスイートが再実行されます。少なくとも1つのテストが失敗すれば、テストがバグを検出したことになり、そのミュータントは殺されます。すべてのテストが依然としてパスすれば、ミュータントは生き残り、テストがそのバグを検出できなかったことを意味します。生き残ったものは、弱いまたは欠けているアサーションを正確に指し示します。
ミューテーションスコア
ミューテーションスコアとは、殺されたミュータントの割合です。カバレッジと異なり、これは単なる実行ではなくアサーションの品質を反映します。高いミューテーションスコアは、テストが実際に振る舞いを検証していることの強力な証拠であり、だからこそより厳しい指標なのです。
簡単な例
ツールが > を >= に変更しても、どのテストも失敗しない場合、境界ケースはテストされておらず、修正する価値のある生き残りです。
if (age > 18) { ... } // original
if (age >= 18) { ... } // mutant: did any test catch it?ミューテーションテストとCIコスト
ミューテーションテストは高コストです。ミュータントごとに1回スイートを再実行するため、通常のテスト実行より桁違いに遅くなることがあります。チームはプッシュごとではなく夜間やスケジュールで実行し、大幅に並列化することがよくあります。ミュータントを高速なrunnerに分散させることが、実現可能に保つ鍵です。
重要なポイント
- ミューテーションテストは、注入されたバグをテストが検出するかを見て採点します。
- 生き残ったミュータントは、弱いまたは欠けているアサーションを明らかにします。
- コストが高いため、スケジュールで実行し、runner間で並列化しましょう。