
AIが書いた単体テストを信用しすぎない|確認ポイント
「テスト、AIに書いてもらった。全部グリーンだし、もう安心」。 そう思った数日後に、テストは通っているのにバグが出た——という経験、ありませんか。あれはけっこう、こたえます。緑のチェックを信じていただけに、なおさら。
単体テストをAIに頼むと、驚くほど速く、それらしいテストが揃います。しかも、たいてい最初から全部通る。ここに、静かな落とし穴があります。「テストが通ること」と「正しさが守れていること」は、別物だからです。
この記事は、AIに単体テストを書かせること自体を止める話ではありません。むしろ、洗い出しや叩き台づくりは得意分野です。ただ、出てきたテストをそのまま信じてしまう前に、何を確かめているのかを自分の目で見る。そのための確認ポイントを、一緒に整理していきましょう。
結論:AIが書いた単体テストは、①何を検証しているか(仕様か、実装の現状か)→ ②アサーションが意味を持っているか(緩すぎないか)→ ③異常系・境界値が入っているか → ④わざと壊して落ちるか(ミューテーション確認)、の4点を自分の目で見てから信用します。緑であることは出発点であって、ゴールではありません。
テストの本当の役割は、「いま動くこと」を見せることではなく、これから壊れたときに教えてくれることです。そこを守れているかを、落ち着いて見ていきます。
なぜ「全部グリーン」が安心材料にならないのか

AIは、聞かれたことに「それらしく動くもの」を返すのが得意です。テストも同じで、まず通ることを優先したテストを書きがちです。それ自体は悪意ではなく、ただの性質です。ただ、その結果として、こんなことが起きます。
- 実装の現状をそのまま写す:いまのコードが返す値を、そのまま「期待値」にしてしまう。仕様ではなく現状をなぞるので、バグもろとも固定される。
- アサーションが緩い:「エラーにならず最後まで動いた」ことだけを見て、戻り値の中身を確かめていない。
- 正常系に偏る:きれいな入力ばかりで、肝心の異常系や境界値が薄い。
- テストのための値が不自然:実際には起きない入力で通していて、現実の使われ方とずれている。
どれも、実行すれば緑になります。だから、結果画面を見ただけでは気づけません。緑は「いまこのテストのとおりに動いた」という事実であって、「正しい」とも「これから守れる」とも言っていないのです。
特にやっかいなのが、最初の「実装の現状をそのまま写す」です。もし元のコードにバグがあれば、AIはそのバグを「正しい挙動」と思い込んでテストに固定します。すると、あとで誰かがバグを直したときに、正しい修正のほうがテストで落ちる——という逆転が起きます。これは、テストがバグを守ってしまっている状態です。
確認の4ステップ(緑になった後にやること)
先に全体像を出します。テストが通ったあと、信用する前に、この順で目を通します。
| ステップ | 見るところ | ひとことで言うと |
|---|---|---|
| ①何を検証しているか | 期待値の出どころ | 「仕様?それとも現状?」 |
| ②アサーションの強さ | 何を確かめているか | 「中身まで見てる?」 |
| ③異常系・境界値 | ケースの幅 | 「壊れ方も試してる?」 |
| ④わざと壊す | テストの感度 | 「バグを入れたら落ちる?」 |
ポイントは、①と④です。①で「仕様を守るテストか、現状をなぞるテストか」を見分け、④で「そもそもこのテストはバグに反応するのか」を確かめます。1つずつ見ていきましょう。
ステップ1:何を検証しているかを見る(仕様か、現状か)
いちばん大事なのは、期待値がどこから来たかです。 テストの「期待する結果」が、仕様(こうあるべき)から来ているのか、実装の現状(いまこう返ってくる)から来ているのか。ここを見分けます。
見分け方は、こう自問することです。
- この期待値は、仕様書や要件を読んで決めた値か?
- それとも、いったんコードを動かして出た値を、そのまま貼っただけか?
後者だと、コードが間違っていても、テストは「正しい」と言い続けます。AIに頼むと、手元のコードの出力を期待値にしてしまうことがよくあるので、ここは必ず人が見ます。
確かめるときは、AIにこう聞くのも有効です。
このテストの各期待値は、仕様のどの部分から導かれていますか。
仕様ではなく現在の実装の出力をそのまま期待値にしている箇所があれば、
正直に指摘してください。
ただ、現場にはきちんとした仕様書が無い・仕様は口頭だけということも多いはずです。その場合は、「仕様のどこから導いたか」のかわりに、関数名と、既存の正常系コードから読み取れる「本来こうしたいはず」という意図を基準にしてかまいません。期待値がその意図とずれていないかを見るだけでも、現状をなぞっただけのテストはかなり見抜けます。
そのうえで、自分が意図を知っている関数を1つ選び、期待値がその意図と合っているかを確かめます。1つでも「あれ、これ現状をなぞってるだけだ」が見つかれば、他も同じ目で見直す合図です。
ステップ2:アサーションが意味を持っているか

次に見るのは、そのテストが結局なにを確かめているかです。 よくあるのが、「例外が出ずに最後まで動いた」だけで満足しているテスト。処理が走りきること自体は確認できますが、戻り値が正しいかは何も見ていません。これは緑になりますが、中身が間違っていても気づけません。
見たいのは、こんな点です。
- 戻り値の中身を確かめているか(型だけでなく、値そのもの)
- 必要な項目すべてを見ているか(一部だけ見て満足していないか)
- 「呼ばれたこと」だけで終わっていないか(モックが呼ばれた、で止まっていないか)
- 期待値がふわっとしすぎていないか(「null でなければOK」のような緩さ)
たとえば、合計金額を返す関数なら、「エラーが出ないこと」ではなく「合計がいくらになるか」を確かめないと意味がありません。AIのテストは、ここが緩いことがあります。
緩いアサーションを見つけたら、「この戻り値の、どの項目まで確認すべきか」を自分で決めて、足りなければ足す。テストは、確かめている範囲の分しか守ってくれません。
ステップ3:異常系と境界値が入っているか
正常系がきれいに並んでいても、壊れ方を試していないテストは、本番で壊れたときに無力です。 ここは、テストケースの洗い出しと同じ視点で見ます。
確認したいのは、最低限この4つの観点が入っているかです。
- 異常系:不正な入力・想定外の状況で、ちゃんと失敗するか
- 境界値:区切りのちょうど上・下・その境目、空、0件で正しく分かれるか
- 状態・順序:前の操作に依存する場合、その組み合わせや順番で崩れないか
- エラーの中身:失敗時に、期待したとおりのエラー(種類・メッセージ)になるか
AIに任せると、ここが薄くなりがちです。足りないと感じたら、観点を指定して追加で出させます。
このテストに、次の観点が抜けていないか確認し、足りなければ追加してください。
- 異常系(不正な入力・想定外の状況で、正しく失敗するか)
- 境界値(上限・下限・ちょうど・空・0件)
- 失敗時に、期待どおりのエラーになっているか
観点ごとに足していくと、「動くこと」を見るテストから、「壊れ方も見張る」テストに変わっていきます。なお、テストケースそのものの洗い出し方はAIにテストケースを洗い出させる|観点の与え方に詳しくまとめました。
ステップ4:わざと壊して、落ちることを確かめる
ここが、いちばん効く確認です。 テストが本当にバグに反応するのかは、実際にコードを壊してみれば分かります。これは「ミューテーション(変異)」と呼ばれる考え方の、手でできる簡易版です。
やり方は単純です。
- テスト対象のコードを、わざと1か所だけ間違える(例:
+を-に、>を>=に、条件を反転させる、戻り値を少しずらす)。 - その状態でテストを走らせる。
- テストが赤くなれば合格。そのテストは、ちゃんとバグを見張れています。
- 壊したのに緑のままなら危険信号。そのテストは、何も守れていません。
- 確認できたら、コードを元に戻す。
「全部グリーン」を見て安心していたテストでも、わざと壊すと緑のままということは、実際よくあります。それは、アサーションが緩いか、肝心の経路を通っていない証拠です。
この手作業は効果が高い一方、毎回やると時間が読めません。そこで、PRごとに必ずやるのではなく、お金・在庫・権限など「壊れたら困るロジック」を触ったときだけ発動する、と決めておくのがおすすめです。それ以外の変更まで毎回やる必要はありません。対象を絞ったうえで、壊れたら困る所を2〜3か所だけ試せば十分、テストの「感度」が見えてきます。AIにこう頼んで、壊しどころを挙げてもらうのも手です。
このコードで、もし1行だけバグを入れるとしたら、影響が大きいのはどこですか。
その変更で、いまのテストが赤くなるかどうかも併せて教えてください。
ただし、ここでもAIの答えを鵜呑みにせず、実際に自分の手で壊して、赤くなるのを目で見て確かめるのが大事です。テストの感度は、机上ではなく実行で確かめます。
ありがちな落とし穴と、その回避
単体テストをAIに任せるとき、つまずきやすい所を先に潰しておきます。
- 緑だから正しいと思う:緑は「このテストのとおり動いた」だけ。何を見ているかを確認する。
- 実装の出力をそのまま期待値にする:バグごと固定される。期待値は仕様から導く。
- 「動いた」で満足するアサーション:戻り値の中身まで確かめる。
- 正常系ばかり:異常系・境界値・状態を観点で足す。
- テスト件数で安心する:数より、わざと壊して落ちるかが本質。
- AIがテストもコードも両方書いた状態を信じる:同じ思い込みが両方に入る。少なくとも片方は人の目で見る。とはいえ全関数は無理なので、金額計算・分岐・外部連携など影響の大きい関数だけ重点的に見れば十分です。
落とし穴のほとんどは、「テストが緑=完了」と捉えた瞬間に生まれます。「緑になってからが確認の始まり」と置き換えるだけで、多くは避けられます。
明日からやること(小さく始める3つ)
いきなり全部やろうとせず、まずこの3つから。
- 次にAIのテストを受け取ったら、期待値を1つだけ仕様と照らす:現状をなぞっていないかを確認します。
- 「動いた」だけのアサーションを1つ見つけて、戻り値の中身を足す:守備範囲が一段広がります。
- 大事な分岐を1か所わざと壊して、赤くなるか見る:テストの感度が分かります。
この3つだけでも、「全部グリーンだけど本当に大丈夫かな」という、あの落ち着かない感じが、少し軽くなります。
コピーして使う「AIテスト確認チェックリスト」
AIが書いた単体テストを受け取ったら、信用する前に上から確認します。全部に○が要るわけではなく、関係する所だけで十分です。重要な関数(金額・在庫・権限・外部連携)を優先し、単純なgetter等は省略可。
① 何を検証しているか
- 期待値は仕様から導かれているか(実装の現状をなぞっていないか)
- 元のコードにバグがあった場合、それを固定していないか
② アサーションの強さ
- 戻り値の「中身(値)」まで確かめているか
- 必要な項目を一通り見ているか(一部だけで満足していないか)
- 「呼ばれた」「エラーが出ない」だけで終わっていないか
③ ケースの幅
- 異常系(不正入力・想定外)が入っているか
- 境界値(上限・下限・ちょうど・空・0件)が入っているか
- 失敗時に、期待どおりのエラーになっているか
④ わざと壊す
- 大事な箇所を1〜3か所わざと壊して、赤くなるのを確認したか
- 壊しても緑のままのテストがなかったか(あれば作り直す)
最後に
テストの正しさを、ひとりで全部背負うのは、しんどいものです。 AIが速く揃えてくれるぶん、「これ、本当に信じていいのか」という確認の負担が、こっそり自分に乗ってきます。その感覚は、たぶん間違っていません。

でも、緑を鵜呑みにせず、「これは何を守ってくれているのか」を一度のぞくだけで、テストは「速くて頼りないもの」から「速くて信頼できるもの」に近づきます。 全部でなくて大丈夫です。今日ひとつ、大事な分岐をわざと壊して赤くなるのを見られたなら、それはもう、テストを信じられる足場ができはじめた証拠です。AIに書かせたコードそのものを見る型はAI生成コードのレビュー・検証チェックリストに、動かして確かめる型は生成コードをそのまま使わない|最低限の動作確認の型に。書かせる・確かめる・見張る。この3つがそろうと、AIとのテストはずいぶん落ち着きます。