AIでテストコードを作るときは、実装だけを渡して「テストを書いて」と頼むのではなく、仕様、境界値、エラー条件、実行コマンドを先に共有します。生成結果は必ず実行し、意図的にバグを入れて検出できるか確認します。
この記事では、小さなJavaScript関数を例に、正常系・境界値・異常系の整理から、Node.js標準テストランナーでの実行、テスト品質の評価までを解説します。
情報確認日:2026年7月18日(日本時間)
結論:AIへ仕様と観点を渡し、生成後に実行して壊す
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コーディングツール比較で解説しています。