本章の方針
2026年、AIがコードを書くのは当たり前になった。GitHub Copilot、Claude Code、Cursor──これらのツールは、自然言語で指示するだけでプロダクションレベルのコードを生成する。
この環境でTDDは不要になったのか? 逆だ。AI時代だからこそTDDの価値は上がっている。ただし、その理由は「AIが書いたコードにもテストが必要だから」という表面的な話ではない。
本章では、Kent Beck が提唱する「Augmented Coding」の概念と、AI + TDD の具体的なワークフローを扱う。
「自分の答案を自分で採点する」問題
Ch.2 で伏線を張った話を回収する。
AIにコードを書かせた後、同じAIにテストを書かせるとどうなるか。
開発者:「Subscription のキャンセル機能を実装して」
AI :(キャンセル機能を実装する)
開発者:「テストも書いて」
AI :(自分が書いた実装に合わせてテストを書く)
結果 :テストは通る。しかし、テストは「実装が正しいか」ではなく
「実装が自分の書いた通りに動くか」を検証しているだけ。
これは自分の答案を自分で採点しているのと同じだ。実装にバグがあっても、テストはそのバグを正しい挙動として追認する。
// AIが書いた実装(バグあり)
function calculateProration(
daysUsed: number,
totalDays: number,
monthlyPrice: number,
): number {
return (daysUsed / totalDays) * monthlyPrice // 小数点の丸めが考慮されていない
}
// 同じAIが書いたテスト(バグを追認している)
test('比例按分を計算する', () => {
const result = calculateProration(15, 30, 1000)
expect(result).toBe(500) // たまたま割り切れるケースだけテスト
})
// → daysUsed=10, totalDays=30, monthlyPrice=1000 だと
// 333.333... になるが、通貨として不正な値。このバグは検出されない。
テスト先書きが解決する理由
テストを先に書くと、人間が「仕様としてどうあるべきか」を定義する。
// 人間が先に書いたテスト(仕様として定義)
test('比例按分は円未満を切り捨てる', () => {
const result = calculateProration(10, 30, 1000)
expect(result).toBe(333) // 333.33... → 333円(切り捨て)
})
test('比例按分の結果は整数(Money型で扱える値)になる', () => {
const result = calculateProration(1, 3, 100)
expect(result).toBe(33) // 33.33... → 33円
})
テストが先にあれば、AIは「テストを通す実装」を書く。テストが仕様を定義しているから、AIは仕様に合った実装を書かざるを得ない。テストが先にある限り、AIはズルができない。
Kent Beck の Augmented Coding
Kent Beck は2025年、AI を活用した開発スタイルを2つに分類した。
Vibe Coding(バイブコーディング)
・コードの品質を気にしない
・「動けばいい」がゴール
・エラーが出たらAIに丸投げ
・テストは書かない、または後付け
・リファクタリングはしない
向いている場面:
・個人のプロトタイプ
・使い捨てのスクリプト
・学習目的の実験
Augmented Coding(拡張コーディング)
・コードの品質を維持する
・テスト、複雑度、可読性を従来と同じ基準で管理する
・AIが書いたコードを人間がレビューし、リファクタリングする
・TDDサイクルの中でAIを活用する
・人間はタイピングが減るだけで、設計判断は人間が握る
向いている場面:
・本番に入れるプロダクションコード
・チームで開発するプロジェクト
・長期間メンテナンスするシステム
Beck は B+ Tree ライブラリの実装実験で Augmented Coding を実践した。約4週間で Rust と Python の両方で本番品質に近い B+ Tree を完成させた。標準ライブラリ(Rust の BTreeMap、Python の SortedDict)に匹敵するパフォーマンスを達成している。
彼がこの実験で強調したのは以下の3点だ。
Beck の Augmented Coding 原則:
1. TDD を厳格に守る(Red → Green → Refactor)
2. AIが勝手にテストを削除・変更することを禁止する
3. 構造の変更(Refactor)と振る舞いの変更(機能追加)を分離する
AI + TDD の具体的ワークフロー
ワークフロー1:人間がテスト、AIが実装
最もシンプルで効果的なパターン。
1. 人間がテストを書く(Red)
2. AIにテストを見せて「このテストを通す実装を書いて」と指示する
3. AIが実装を書く(Green 候補)
4. テストを実行して Green を確認する
5. 人間がコードをレビューし、必要ならリファクタリングする(Refactor)
6. 1に戻る
// 1. 人間が書いたテスト
test('トライアル期限切れ後はアクティベートできない', () => {
const sub = Subscription.startTrial('customer-1', 'plan-basic')
sub.expireTrial() // トライアル期限切れ
expect(() => sub.activate()).toThrow('期限切れのトライアルはアクティベートできません')
})
// 2. AIへの指示:「このテストを通す実装を書いて」
// 3. AIが書いた実装(人間がレビュー)
このワークフローの鍵は、人間が「何を作るか」を定義し、AIが「どう作るか」を実装するという役割分担だ。
ワークフロー2:AIがテスト案、人間が取捨選択
テストリスト(Ch.4)の作成をAIに手伝ってもらうパターン。
1. 人間が「Subscription のキャンセルに関するテストケースを列挙して」と指示
2. AIがテストケースの候補リストを生成する
3. 人間がリストを取捨選択・修正する
4. 選んだテストケースを人間がコード化する(またはAIに書かせてレビュー)
5. 通常のRed-Green-Refactorに入る
AIが生成したテストケース候補(例):
□ アクティブな契約をキャンセルできる ← 採用
□ トライアル中の契約をキャンセルできる ← 採用
□ キャンセル済みの契約は再キャンセルできない ← 採用
□ キャンセル時にメールが送信される ← 保留(副作用はイベントで分離)
□ キャンセル後に請求が停止される ← 保留(別のユースケースのテスト)
□ 日曜日にキャンセルしても問題ない ← 削除(曜日は関係ない)
AIは網羅的にケースを列挙するが、取捨選択は人間が行う。テストの優先順位、スコープの判断、「今必要か」の判断は人間の仕事だ。
ワークフロー3:マルチエージェントTDD
2026年に登場した先進的なアプローチ。Red / Green / Refactor のフェーズを別々のAIエージェントに担当させる。
Red Agent :テストだけを書く。実装を知らない。
Green Agent :テストを見て実装を書く。テストの書き方を知らない。
Refactor Agent:実装とテストを見てリファクタリングする。
なぜ分離するか:
・1つのコンテキストで全フェーズを実行すると、
テスト設計が実装に引きずられる(AIの「カンニング」問題)
・別エージェントにすることで、テストと実装の独立性を保てる
このアプローチはまだ実験的だが、TDDの原則(テストと実装の独立性)をAIアーキテクチャに直接反映した点で注目に値する。
AI時代のTDDが解決する3つの問題
問題1:AIのハルシネーション検出
AIは自信を持って間違ったコードを書く。テストがなければ、間違いに気づけない。
AIなしの世界:人間がバグを作る → テストで検出
AI時代の世界:AIがバグを作る → テストで検出
AIが「正しそうに見える」バグを作る → テストでしか検出できない
AIが書いたコードは、人間が書いたコードよりも**「正しそうに見える」**ことが多い。変数名が適切で、コメントが丁寧で、構造がきれい──だが、エッジケースでバグがある。テストだけが、この「正しそうに見えるバグ」を検出できる。
問題2:AIのコンテキスト管理
LLM は巨大なコンテキストを扱えるが、コンテキストが大きくなるほど精度が落ちる。TDD の小さなサイクルは、AIに「一度に1つのこと」を考えさせる。
❌ 大きな指示:
「SaaS課金管理のSubscriptionを実装して。
状態遷移、プラン変更、イベント発行、永続化も含めて」
→ コンテキストが大きすぎて、各部分の品質が下がる
✅ TDD的な小さな指示:
「このテストを通して」(1サイクル分だけ)
→ 集中した高品質な実装が得られる
問題3:AIの「暴走」防止
AIエージェントは指示されていないことまで実装しがちだ。Kent Beck も「AIがテストを勝手に削除してパスさせようとする」問題を報告している。
AI暴走の典型パターン:
・テストが失敗 → テストを削除して「通った」と報告
・1機能を頼んだ → 5機能分のコードを書く
・リファクタリングを頼んだ → 振る舞いも変えてしまう
TDDが防止する方法:
・テストが失敗 → テストは人間が管理。削除させない
・1テストずつ進む → 1機能ずつしか実装が進まない
・Refactor中はテストが通り続ける → 振る舞いの変更を即座に検知
Before / After:同じ機能をAIに実装させる
TDD あり/なしで、AIに同じ機能を実装させた結果を比較する。
TDDなし
指示:「Money クラスを作って。通貨付きの金額で、加算と減算ができるようにして」
AIの出力:
・Money クラス(200行)
・加算、減算、乗算、除算、フォーマット、通貨変換メソッド
・JSON シリアライズ/デシリアライズ
・ロケール対応のフォーマッタ
→ 頼んでいない機能が大量に含まれている
→ 通貨チェックが一部のメソッドで抜けている
→ テストなし
TDDあり
指示:「このテストを通して」(1本ずつ)
AIの出力:
・Money クラス(40行)
・テストが要求した機能だけが実装されている
・全てのメソッドにテストがある
・YAGNI が守られている
テストが仕様書として機能し、AIの出力を制御している。TDD のテストは、AIにとっての「ガードレール」になる。
実践Tips:AI + TDD で気をつけること
1. テストは自分で書く(少なくともレビューする)
→ AIにテストを全部任せると「自分の答案を自分で採点」問題に陥る
2. Green になったらAIの出力を必ずレビューする
→ 「テストは通るが品質が低い」コードをAIは書く
3. リファクタリングは人間が判断する
→ AIにリファクタリングを任せると、振る舞いも変えてしまうことがある
4. テストを削除・変更する権限をAIに与えない
→ Beck の教訓。AIはテストを削除して「問題を解決」しようとする
5. 1サイクルずつ進む
→ AIに大きな指示を出さない。1テストの Red → Green → Refactor で1サイクル
TDD の未来:テストが「意図の言語」になる
AI時代のTDDは、単なるテスト手法を超えて、**人間とAIの間の「意図の共有言語」**になりつつある。
従来のTDD:
人間がテストを書く → 人間がコードを書く → テストが品質を保証する
AI時代のTDD:
人間がテストを書く → AIがコードを書く → テストが意図との整合性を保証する
テストは「コードが正しいか」を検証するだけでなく、「人間の意図がAIに正しく伝わったか」を検証する手段になった。テストは意図の実行可能な表現だ。
これは Kent Beck が52年のキャリアの末に辿り着いた結論でもある。「“何が正しいか”を定義し、“正しさ”を実行可能な形式で表現する」──それが TDD の本質であり、AI時代にこそ輝く理由だ。
本章のまとめ
| 要点 | 内容 |
|---|---|
| 自己採点問題 | AIが実装→AIがテスト は「自分の答案を自分で採点」と同じ |
| Augmented Coding | コード品質を維持しながらAIを活用する(Beck提唱) |
| ワークフロー | 人間がテスト+AIが実装 が最もシンプルで効果的 |
| AIの暴走防止 | テストがAIのガードレールになる。削除権限は与えない |
| テストの新しい意味 | テストは人間とAIの間の「意図の共有言語」 |
次章──最終章では、ここまでの旅を振り返る。テストで駆動してきた設計の全体像を眺め、ある発見について話す。