0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIにテスト書かせて満足してない? カバレッジを「測る」だけで、抜けが全部見える

0
Last updated at Posted at 2026-06-25

ベンチャー開発のような、テストなんてろくに書いてこなかった。バグが出たら直す、それで回してきた方へ。。。
このやり方、運用チームの負担が尋常じゃない。バグ対応に追われて、適当にマージして「結果動かないじゃないか!」となった案件は多いのではなかろうか?
ましてやAIコーディングの時代なのでますます運用が難しくなっているだろう。

で、思ったわけだ。コーディングAgentがいる時代に、人間がテストを書く意味ある?
テストは全部Agentに書かせて、人間は仕様だけ見る——そういう運用にゴリ押しでもよくないか、と。

この記事は、その発想で「運用者が限界まで手を抜くにはどうするか」を突き詰めた話だ。
鍵は、AIに書かせたテストの “充足”をどう担保するか。結論から言うと、レビューを「実装」から「テスト」へ移し、その十分さは「カバレッジを測る」だけで大きく前進する。

そもそも、テストのカバレッジって「測れる」の知ってた?

「テスト書いてる」けど「どれだけ網羅できてるかは測ったことない」という方は多いのではなかろうか?

vitestなら --coverage を足すだけ。

npx vitest run --coverage

すると、どの行・どの分岐をテストが通っていないかが表でバッと出る。これだけで「自分のテスト、ここ抜けてたんだ」が目で見えるようになる。AIにテストを書かせた時こそ、これが効く。

実際にやってみよう。

題材:ありがちな送料計算

shipping.ts
// 仕様: 5000円以上は送料無料。北海道・沖縄は1000円、それ以外は500円。
export function shippingFee(prefecture: string, amount: number): number {
  if (amount >= 5000) return 0;
  if (prefecture === "北海道" || prefecture === "沖縄") return 1000;
  return 500;
}

「ちゃんとテストして」だと人もAIも漏らすので、テストケースは機械的に並べる。要は境界と分岐を全部突くだけ。

prefecture amount 期待
東京 5000 0 ← 無料の境界ちょうど
東京 4999 500 ← 境界の外
北海道 3000 1000 ← 離島
沖縄 3000 1000 ← 離島
東京 3000 500 ← 通常

この表が、このあと全部の「正」になる。人間がレビューするのはこの表だけでいい、というのがこの記事のオチに繋がる。

AIに「とりあえず動く」テストを書かせると、こうなる

AIに雑に振ると、だいたい“それっぽく緑になる”テストが返ってくる。

shipping.test.ts
import { describe, it, expect } from "vitest";
import { shippingFee } from "./shipping";

describe("shippingFee", () => {
  it("5000円以上は送料無料", () => {
    expect(shippingFee("東京", 5000)).toBe(0);
  });
  it("通常は500円", () => {
    expect(shippingFee("東京", 3000)).toBe(500);
  });
});

実行すると——テストは緑。2件ともパス。 普通ならここで「OK」と言ってマージしたくなる。

ここで --coverage を足す。すると、こうだ(実測)。

 Test Files  1 passed (1)
      Tests  2 passed (2)

File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|------------------
 shipping.ts |      80 |   83.33  |   100   |   100   | 4

緑なのに、Branchは83%。Uncovered Line #s に「4」。
4行目は 北海道・沖縄なら1000円 の分岐だ。つまりAIのテストは離島ケースを一度も通っていない。緑なのに、仕様の3分の1が抜けていた。

テストが緑かどうかは「書いたテストが通ったか」しか言わない。何をテストし忘れたかは、カバレッジを測らないと見えない。

抜けを埋める

カバレッジが指した「4行目(離島)」と、ついでに境界(4999)を表から足す。

shipping.test.ts
import { describe, it, expect } from "vitest";
import { shippingFee } from "./shipping";

describe("shippingFee", () => {
  it.each([
    ["東京", 5000, 0],
    ["東京", 4999, 500],
    ["北海道", 3000, 1000], // ← 抜けてた離島分岐
    ["沖縄", 3000, 1000],
    ["東京", 3000, 500],
  ])("%s %i円 → %i円", (pref, amount, expected) => {
    expect(shippingFee(pref, amount)).toBe(expected);
  });
});

再実行(実測)。

      Tests  5 passed (5)
Statements   : 100% ( 5/5 )
Branches     : 100% ( 6/6 )

Branch 100%。 これで「仕様の分岐は全部通した」と数字で言える。
ポイントは、カバレッジ100%を目標にするんじゃなく、「抜けを見つける道具」として使うこと。表(仕様)を正として、抜けを潰すために測る。

で、レビューはテストだけ見ればよくなる——が、罠が1つ

ここまで来ると、人間の仕事は「テスト表(=仕様)のレビュー」と「カバレッジの確認」にほぼ集約される。実装の行を追わなくてよくなる。これがやりたかったこと。

ただしでかい罠がある。AIに実装とテストを“同時に”書かせると、テストが実装に寄った“同義反復”になる。実装のバグごとテストが追認して、緑なのに間違う。

対策はシンプル。

  1. テストは仕様から書く。実装を見せない。 さっきの表だけが正。
  2. テストを先に確定させてから実装させる(テストファースト)。
  3. できれば実装とテストを別セッション/別エージェントで書かせる。互いのバグを共有させない。

個人的には、AIに「まずこの表を満たすテストだけ書いて」と渡し、通ることを確認してから実装させるだけで、だいぶ事故が減った。

まとめ(運用フロー)

1. 仕様 → テストケースを機械的に列挙   ← 人間がレビューするのはここ
2. AIにテストを書かせる(実装は見せない/テストファースト)
3. AIに実装を書かせて緑にする
4. vitest --coverage で「抜け」を確認   ← 足りなければ表に足してループ
5. PRは「テスト+カバレッジ」を見る。実装は緑なら通す

ツール:vitest/--coverage(裏は V8 or istanbul)。まずはこれだけでいい。

限界

  • E2E・性能・セキュリティ・探索的テストはこの枠の外。
  • そして大事な但し書き:カバレッジ100%=完璧、ではない。「全部の行を通った」だけで、「その行が正しいか」までは保証しない。>=> に間違えても100%のまますり抜けることがある。
    • そこを潰すミューテーションテストという次のレイヤーがあり、そういう部分まで行くとコードレビューの対象が大幅に減り、本質的な仕様に重きを置くことができるようになる。

それでも、「実装を全部読む」から「テスト表とカバレッジを見る」に重心を移すだけで、AIコードのレビューは本当に別物になる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?