■ 1. 発表概要
- 発表者: 伊藤瑛(DeNA エンジニアリング室 開発デザイングループマネージャー)
- イベント: Go Junction #1(2026年3月23日)
- テーマ: Goにおけるテスト品質測定手法としてのMutation Testingと、独自ツール「rmut」の設計・実装
■ 2. Mutation Testingの概念
- 定義: 意図的な不具合(ミュータント)をコードに埋め込み、テストの失敗可否でテスト品質を測定する手法
- 用語:
- survived: ミュータント埋め込み後もテストが通過した状態
- killed: ミュータント埋め込みによりテストが失敗した状態
- カバレッジとの違い: カバレッジはテストの通過確認のみを行うが、Mutation Testingは不具合検出能力を測定する
■ 3. ユースケース
- 年齢判定の例:
>を>=に変更しても既存テストが通過する問題を検出- テストの不十分さを機械的に発見できる
- Query Builderの例:
- 複雑なSQL条件テストにおけるテストデータの網羅性を確認できる
- 検出可能な問題:
- テストデータ・フィクスチャの網羅性不足
- Assertionの正確性欠如
- ライブラリ動作の誤解(Equalsの振る舞いなど)
■ 4. rmutの設計と実装
- 高速化アプローチ:
- 1回のAST書き換えで全ミューテーションを埋め込む
- TestMainでテストをループさせ、ループごとに異なるmutatorを起動させる
- 通常の「mutant1つごとにTest Suite全体を実行」する構造を改善
- 実装上の課題:
- 型情報の確認: 書き換え対象式の型をpackages.Loadで取得する必要がある
- Untyped型への対応: リテラル値だけからは型判定ができない場合がある
- Compile Error回避: 親ノードのコンテキスト分析が必須
- 無限ループ対策:
- LoopCounter: for文検出時にループ回数制限を埋め込む
- Timeout: Go標準の機構を活用
- 再帰やgotoによる無限ループには未対応
- 運用上の工夫:
sync.*系ミューテーションを除外(デッドロック回避)- gitのdiffとcallgraph解析で適用範囲を限定
- カスタムmutator用プラグイン機構(squirrel等の非標準ライブラリ対応)
■ 5. 残存する課題
- Compile Error: 予期しない構文で発生する
- 実行時間の不安定性: ミューテーション数に応じて増加する
- 無限ループの遭遇: パニック回避の限界がある
- Survivedミューテーションの意味: ロギングや例外処理では無意味化する場合がある
- プラグイン配布の困難: go/pluginの制限、WASI対応の困難がある
■ 6. 結論
- テスト品質の評価を「Testの品質を測定しよう」という観点から行うべき
- AI時代においてテスト生成の「正しさ」を計測する手法として重要性が増している
- 参考資源: Stryker Mutator、pitest(Java向けツール)、Google Research「State of Mutation Testing at Google」