AI活用

AIでテストコードを作る方法|正常系・境界値・異常系まで検証する

AIへテストコードを作らせる手順を、仕様整理、正常系・境界値・異常系の列挙、実行、意図的なバグ検出、品質評価まで解説します。

この記事の目次
  1. 結論:AIへ仕様と観点を渡し、生成後に実行して壊す
  2. 1.テスト対象の契約を決める
  3. 2.正常系・境界値・異常系を列挙する
  4. 3.実行環境を指定してテストを生成する
  5. 4.失敗を仕様・実装・テストへ分ける
  6. 意図的なバグでテストを評価する
  7. AI生成テストのレビュー項目
  8. AIでテストコードを作るときのよくある質問
  9. テストをすべてAIへ任せてもよいですか?
  10. カバレッジ100%なら十分ですか?
  11. UIテストも生成できますか?
  12. まとめ

AIでテストコードを作るときは、実装だけを渡して「テストを書いて」と頼むのではなく、仕様、境界値、エラー条件、実行コマンドを先に共有します。生成結果は必ず実行し、意図的にバグを入れて検出できるか確認します。

この記事では、小さなJavaScript関数を例に、正常系・境界値・異常系の整理から、Node.js標準テストランナーでの実行、テスト品質の評価までを解説します。

情報確認日:2026年7月18日(日本時間)

結論:AIへ仕様と観点を渡し、生成後に実行して壊す

5段階の進め方

  1. 関数の契約を文章にする
  2. 正常系・境界値・異常系を表にする
  3. 使用中のテスト環境に合わせて生成する
  4. 実行し、失敗原因を仕様・実装・テストに分ける
  5. 実装を意図的に壊し、テストが検出するか確かめる
スポンサーリンク

1.テスト対象の契約を決める

例として、購入金額から送料を返す関数を考えます。

export function calculateShipping(total) {
  if (!Number.isFinite(total) || total < 0) {
    throw new TypeError("total must be a non-negative finite number");
  }

  return total >= 5000 ? 0 : 500;
}

コードだけでなく、次の契約をAIへ渡します。

  • 入力は0以上の有限な数値
  • 5,000円未満の送料は500円
  • 5,000円以上は送料無料
  • 負数、NaN、InfinityはTypeError
  • 外部状態や時刻には依存しない

2.正常系・境界値・異常系を列挙する

分類 入力 期待結果 理由
正常系 1,200 500 通常の送料あり
正常系 8,000 0 通常の送料無料
境界値 0 500 許容される最小値
境界値 4,999 500 しきい値の直前
境界値 5,000 0 しきい値そのもの
境界値 5,001 0 しきい値の直後
異常系 -1、NaN、Infinity TypeError 契約外の入力

AIは代表値を増やしがちですが、同じ分岐を通る似た値を大量に並べても効果は限定的です。分岐、境界、失敗条件を優先します。

3.実行環境を指定してテストを生成する

次の契約と実装に対するテストを作成してください。

環境:
- Node.js
- ES Modules
- node:test
- node:assert/strict
- 外部パッケージは追加しない

必要なケース:
- 正常系
- 0、4999、5000、5001の境界値
- 負数、NaN、Infinityの異常系

出力:
1. shipping.test.jsのコード
2. 各ケースが必要な理由
3. 実行コマンド

禁止:
- 実装の変更
- 未確認の依存関係の追加

生成例は次のようになります。

import test from "node:test";
import assert from "node:assert/strict";
import { calculateShipping } from "./shipping.js";

test("5,000円未満は送料500円", () => {
  assert.equal(calculateShipping(1200), 500);
});

test("境界値の前後で送料が切り替わる", () => {
  assert.equal(calculateShipping(0), 500);
  assert.equal(calculateShipping(4999), 500);
  assert.equal(calculateShipping(5000), 0);
  assert.equal(calculateShipping(5001), 0);
});

test("0以上の有限数以外は拒否する", () => {
  for (const value of [-1, Number.NaN, Number.POSITIVE_INFINITY]) {
    assert.throws(
      () => calculateShipping(value),
      { name: "TypeError" }
    );
  }
});
node --test shipping.test.js

4.失敗を仕様・実装・テストへ分ける

テストが失敗したら、AIへ修正を丸投げせず、原因を分類します。

原因 対応
構文・環境 CommonJSとES Modulesの混在 package設定と既存テストに合わせる
仕様 5,000円を含むか不明 担当者が契約を確定する
実装 >=>になっている 仕様に基づき実装を直す
テスト 例外型を誤って期待 契約と既存方針に合わせて直す

避けたい対応:テストを通すためだけに実装の仕様を変えることです。実装とテストのどちらが正しいかは、契約と利用側の期待から判断します。

意図的なバグでテストを評価する

実装の比較演算子を一時的に次のように変えたとします。

// 意図的なバグ:5,000円ちょうどが送料500円になる
return total > 5000 ? 0 : 500;

この変更では、calculateShipping(5000)のテストが失敗する必要があります。検出できれば境界値テストが機能しています。検出できない場合は、行カバレッジが高くても重要な条件を守れていません。

検証ログ
変更: >= を > に変更
期待: 5000のケースが失敗
結果: 期待どおり失敗
判定: 境界値の変異を検出できた
復旧: 元の実装へ戻し、全テスト成功を確認

この考え方を自動化したものがMutation Testingです。演算子や条件を小さく変え、テストが失敗すれば「killed」、通り抜ければ「survived」と判定します。導入時は実行時間も測り、重要な領域から対象にします。

AI生成テストのレビュー項目

  • テスト名から期待する振る舞いが分かるか
  • 正常系、境界値、異常系が契約に対応しているか
  • 実装内部ではなく公開された振る舞いを確認しているか
  • 時刻、乱数、ネットワーク、DBを必要に応じて固定・分離しているか
  • 各テストが独立し、実行順に依存しないか
  • モックが多すぎて実際の連携を検証できなくなっていないか
  • 失敗メッセージから原因を追えるか
  • 存在しないAPIや依存関係を作っていないか

コード差分全体の確認にはAIコードレビューの差分チェックリストも利用できます。

AIでテストコードを作るときのよくある質問

テストをすべてAIへ任せてもよいですか?

おすすめしません。ケース列挙と雛形作成は効率化できますが、仕様の確定、期待値、重大な失敗、実行結果は人が確認します。

カバレッジ100%なら十分ですか?

十分とは限りません。行が実行されても期待値が弱ければバグを見逃します。境界値、実際の失敗履歴、Mutation Testingなどで検出力を補います。

UIテストも生成できますか?

可能ですが、要素の特定方法、待機、テストデータ、後片付けが必要です。E2E運用例はClaude Code HooksとPlaywrightも参考になります。

まとめ

AIでテストコードを作るなら、実装を渡す前に契約とケース表を用意し、使用中のテスト環境を指定します。生成後は実際に実行し、失敗を仕様・実装・テストへ分類してください。

最後に意図的なバグを入れ、重要な境界値を検出できるか確かめます。AIコーディング環境の選び方はAIコーディングツール比較で解説しています。

スポンサーリンク