AIにテストを書かせるときの注意点:緑でも正しいとは限らない
実装コードと同じように、テストコードもAIに書かせることが増えました。「この関数のテストを書いて」と頼めば、数秒でそれらしいテストが返ってきます。カバレッジも数字の上では伸びるし、CIは緑になる。作業としては速いです。
ただ、緑のテストが並んでいるのを見ると、別の不安が残ります。「このテストは、本来守るべき仕様を守れているのか」。AIに書かせたテストには、人間が書くときとは少し違う入り方の問題が混ざりやすいと感じています。この記事は、その問題のパターンと、頼み方・レビューでどう潰しているかを整理したものです。なお、ここで扱うのは主に単体テスト寄りの話で、E2E側の最小構成は AstroサイトのE2EはPlaywrightの最小構成から に分けています。
起きやすい問題パターン
まず、よく見かける5つのパターンを表にまとめます。
| パターン | なぜ起きるか | 見抜き方 |
|---|---|---|
| 仕様ではなく現状をなぞるテスト | AIは手元の実装を読んで「今の出力」を期待値に置くのが得意で、仕様書よりもコードを正としてしまう | 期待値が仕様と一致するか。仕様を変えたら落ちるはずのテストが、そのまま通っていないか |
| モックで固めて実物と切り離される | 外部依存を全部モックにするとテストは安定して速くなるので、AIはその方向に寄せがち | モックの戻り値が実物の仕様とずれていないか。モック同士を検証しているだけのテストになっていないか |
| 期待値そのものが間違っている | 計算や変換のルールをAIが推測で決めてしまう。テスト自体は整合しているので気づきにくい | 期待値の根拠を説明させているか。自分で検算できる値は検算したか |
| 失敗ケース・境界値が抜ける | 正常系は仕様から読み取りやすいが、異常系は明示しないと出てこない | 空・0・上限ちょうど・上限+1・エラー時のケースがあるか |
| テストが通るように実装を寄せる | 「テストを通して」と頼むと、実装側をテストに合わせて直してしまうことがある | 実装の差分が、仕様に照らして正しい変更か。テストと実装の両方が動いていないか |
たとえば消費税の計算です。仕様が「税率10%、端数は切り捨て」だとします。
// 仕様: 切り捨てのはずが、実装が切り上げになっている
export function withTax(price: number): number {
return Math.ceil(price * 1.1);
}
この実装を渡してテストを書かせると、withTax(198) の期待値に218(切り上げた結果)が入りがちです。テストは緑になりますが、守っているのは仕様ではなくバグのほうです。しかも後から実装を仕様どおりに直すと、このテストが「壊れた」と騒ぎ出す。なぞりテストの厄介なところは、間違いを固定する方向に働く点です。
ポイント: AIが書いたテストで一番疑うべきは、文法や書き方ではなく「期待値の出所」です。期待値が実装から来たのか、仕様から来たのかで、そのテストの価値はまったく変わります。
頼み方で先に潰す
問題の多くは、レビューで拾うよりも、頼む段階で先回りしたほうが安く直せます。自分がやっているのはこの3つです。
- 仕様を先に渡す。 コードだけ渡して「テストを書いて」と頼まない。期待する振る舞い(入力と出力の対応、端数の扱い、エラー時の動き)を文章で添えてから頼む。仕様があると、AIは現状なぞりではなく仕様基準でケースを組み立てます
- 失敗ケースを明示させる。 「正常系だけでなく、失敗ケースと境界値を最低これだけ含めて」と、観点をこちらから指定する。任せると正常系ばかりになるので、不足を前提に頼むほうが結果的に速いです
- 期待値の根拠を説明させる。 テストと一緒に「なぜこの期待値になるか」をケースごとに一言添えさせる。説明が曖昧なケースは、そのまま期待値を間違えている可能性が高い。レビューで最初に見るのもここになります
逆に、頼み方として避けているのは「テストが通るように直して」という指示です。実装のバグなのかテストの間違いなのかを切り分けないまま頼むと、両方を少しずつ寄せて辻褄を合わせた変更が返ってくることがあります。切り分けは人がやって、直す対象を片方に絞ってから頼むほうが安全です。
レビューで見る観点
返ってきたテストは、実装コードとは違う観点でレビューします。使っているチェックはこんな形です。
- 期待値はすべて仕様から説明できるか(実装の現状を根拠にしていないか)
- 失敗ケースと境界値が、仕様の範囲をカバーしているか
- モックは「遅い・不安定」な依存だけに絞れているか。検証対象そのものをモックしていないか
- テスト名から、何を守るテストか読み取れるか
- 仕様変更で落ちるべきテストが、本当に落ちる作りになっているか(試しに期待値をずらして確認したか)
- スナップショットや現状固定の検証が、意図せず大量に入っていないか
最後の「わざと落としてみる」は地味ですが効きます。期待値を少しずらしてテストが赤くなるかを見るだけで、「何も検証していないのに通っているテスト」をかなり拾えます。緑のままだったら、そのテストは飾りです。
緑は「正しさ」ではなく「矛盾のなさ」
線引き: テストが緑であることは、「テストと実装が矛盾していない」ことを示すだけです。両方が同じ間違いを共有していれば、緑のまま間違いは残ります。
AIにテストを書かせると、この共有された間違いが起きやすくなります。実装を読んで期待値を決めるので、実装の思い込みがテストにもコピーされるからです。人間が書くテストにも同じ問題はありますが、「仕様とコードを別々に見る」という工程が自然に入りやすい分、AI一任のときほど意識して分ける必要があります。
なので、自分の使い方は「テストの下書きはAI、期待値の決定と失敗ケースの選定は人」という分担に落ち着きました。生成コードのレビュー全般でも同じですが、AIに任せてよいのは作業量の部分で、「何が正しいか」の判断まで渡すと、緑のテストと一緒に間違いまで固定されてしまいます。速さはそのままに、判断だけ手元に残す。そのための頼み方とチェックが、この記事に書いたことです。