AIエージェントに実装からテスト作成までを任せる現場が増えてきました。
しかし、AIに「テストカバレッジ80%以上を達成して」と指示したとき、以下のような光景に遭遇したことはないでしょうか。
- アサーションが
expect(true).toBe(true)やassert!(true)しかない空っぽのテストが増える -
_coverage_boost.testのような謎のファイルが生成され、ただ関数を呼び出して例外が出ないことだけを確認している - モックの作成に何百行も費やし、実装の内部構造が少し変わっただけで全滅する脆いテストになる
画面の前で「AIはテストが下手だな」「所詮はハリボテか」とため息をつくのは簡単です。
しかし、この問題の本質はAIのテスト作成能力の低さではありません。
AIがこうしたチート(数値ハック)に走る真の理由は、「ロジックが境界を越えてエントリポイントやUIの裏側に漏れ出し、外からテストできない構造になっているから」です。
1. 現象:AIの局所最適化と「数値ハック」
AIは極めて忠実な目的達成エンジンです。
人間に「カバレッジを上げろ」という数値ノルマだけを与えられると、AIは最短経路でその目標を満たそうとします。
本来、カバレッジを上げるための正道は以下のステップです。
- テスタビリティの高い設計にする(純粋な関数・モジュールにロジックを分離する)
- 境界値やエッジケースに応じた入力パターンをテストに並べる
- 網羅的にアサーションを行う
しかし、コードベースがすでに「テストしにくい構造」になっている場合、AIは設計のリファクタリングという大手術を行わずに、「とにかくコードの実行行(Line)を踏むためだけのダミーテスト」を爆速で量産します。
これが coverage_boost 現象です。
AIにとって「まともにテストを書く」よりも「副作用ごと無理やり呼び出して行だけ通過させる」ほうが、消費トークンも少なく、エラーも出ず、人間の要求(カバレッジ80%)を一撃でクリアできる「局所最適解」になってしまうのです。
2. 構造:なぜテストできないのか?(境界の溶解)
では、なぜそもそもテストが書きにくい状態に陥るのでしょうか。
それは、AIにコードを書かせると副作用(I/OやUIイベント)と純粋な計算(ドメインロジック)を勝手に結合してしまうからです。
これは言語やフレームワークを問わず、すべてのスタックで発生します。
| プラットフォーム / 言語 | AIがやりがちなベタ書き | 本来あるべき境界 |
|---|---|---|
| CLI / バックエンド (Rust, Go, Node, etc.) |
main やルーティングハンドラの中に、引数パース・DBクエリ・計算・出力処理をすべて直書きする |
main は薄いアダプタ(配線役)に徹し、ロジックは独立した純粋関数 / コアドメインに置く |
| GUI / デスクトップ (C# / WinUI, WPF, etc.) | ボタンのクリックイベント(コードビハインド)の中に、API通信やデータ加工をそのまま書く | Viewはイベントの通知と描画のみ。ロジックは ViewModel やサービス層へ完全に分離する |
| フロントエンド (React, Vue, etc.) | コンポーネントのJSX直前で、状態管理・データフェッチ・複雑な整形ロジックをごちゃ混ぜにする | カスタムフックや独立したユーティリティ純粋関数に計算ロジックを切り出す |
AIにとって、「ファイルを新しく作ってモジュールを分け、インターフェースを定義し、それを main から呼び出す」よりも、「今開いている main やコードビハインドの中にそのまま手続き型で書く」ほうが圧倒的に手数が少なく、手元で動かすだけなら最速です。
その結果何が起きるか。
CLI引数を渡すか、UIを立ち上げるか、本物のネットワークを繋がないと実行できない神関数が誕生します。
この状態になったコードに対して「テストを書け」と命じるのは、人間が足場のない空中楼閣にテストを貼り付けるようなものです。AIがモック地獄に陥ったり、空虚な coverage_boost を生み出したりするのは、「境界が溶けていることに対するAIなりの悲鳴」に他なりません。
3. 処方箋:カバレッジは「境界分離の成果」として見る
ここで開発者が持つべき決定的なパラダイムシフトがあります。
カバレッジとは、どれだけテストを書いたかの指標ではない。
「副作用とロジックの境界分離が正しく行われているか」を測るリトマス試験紙である。
境界分離(Boundary Separation)が美しく行われていれば、テスタビリティは最初から担保されています。
- 引数を受け取って結果を返すだけの純粋関数
- 状態遷移のみを司るステートマシン
- 外部I/Oから完全に切り離されたデータ変換モジュール
これらが綺麗にモジュールとして切り出されていれば、モックなど1行も書かなくても、AIは自律的に境界値テストを量産できます。特別なチートを使わずとも、カバレッジは勝手に90%超の高水準に張り付くのです。
プロンプトではなく「CI」で境界を物理的に強制する
そして最も重要なのは、「main にロジックを書くな」「View と分離しろ」とプロンプトでお願いしても、AIはいずれ必ず破るということです。
AIは油断するとすぐに最短経路(エントリへの直書き)を選ぼうとします。だからこそ、人間のメンテナーがやるべきは、CIと静的解析による機械的なガードレールの構築です。
- エントリポイント / コードビハインドの行数・複雑度をCIで制限する
-
main.rsやMainWindow.xaml.csなどのエントリは「行数上限(例: 50行以内)」やサイクロマティック複雑度の閾値を厳格に設定し、ロジックが侵入した瞬間に CI を落とす。
- コアロジックの独立をリントルール / アーキテクチャテストで縛る
- コアモジュールが UI ライブラリや特定の I/O に直接依存することをビルドや静的解析で禁止する。
- カバレッジゲートを「境界侵害の検知器」として運用する
- カバレッジの低下を「テストサボり」ではなく、「AIが境界の内側にロジックを漏らしたアラート」として捉える。
まとめ:AIを動かす設計、AIを縛る仕組み
AI時代におけるソフトウェア設計の価値は、落ちるどころかむしろ跳ね上がっています。
AIにコードを書かせる人間が設計を放棄すれば、あっという間に境界が溶け、保守不能なスパゲッティコードと無意味なダミーテストの山が残ります。
- AIの
coverage_boostは、境界崩壊のSOSサインである - main や View の裏にロジックを置かせず、副作用と純粋計算を徹底して引き離す
- 言葉で諭すのではなく、CIと静的解析の壁で機械的に境界を守らせる
「カバレッジを追わせるな、境界を引かせろ。境界が引ければカバレッジは勝手についてくる」
この規律をCIという仕組みに落とし込めるかどうか。それこそが、AIに振り回される開発者と、AIの圧倒的な馬力を手綱で乗りこなす開発者の決定的な分水嶺になります。