本章の方針
TDD は万能ではない。
ここまでの章でTDDの効果を実感してきたはずだ。だが、全てのコードにTDDを適用すべきとは限らない。TDDが逆効果になる場面がある。それを知らずにTDDを教条的に適用すると、開発速度が落ち、チームの士気が下がり、「やっぱりTDDは使えない」という結論に至る。
本章では、TDDの限界を正直に示し、「使う / 使わない」の判断軸を提供する。
TDDが有効な領域
まず、TDDが最も効果を発揮する領域を確認する。
TDDが有効な領域:
✅ ビジネスロジック(計算、状態遷移、ルール検証)
✅ ドメインモデル(Money、Subscription のような型)
✅ アルゴリズム(ソート、検索、変換)
✅ データ変換・バリデーション
✅ API のリクエスト/レスポンスハンドリング
✅ 明確な入出力がある関数
共通する特徴は、入力と出力が明確で、正解が定義できることだ。1000円 + 500円 = 1500円。trial → active は可能、canceled → active は不可。これらは正解がはっきりしている。
TDDが逆効果になる領域
1. UIの探索的デザイン
場面:「どんなUIにするか」を試行錯誤している段階
なぜTDDが合わないか:
・正解が事前に定義できない(「見た目が良いか」は主観)
・UIの変更サイクルが速すぎてテストが追いつかない
・テストが変更のブレーキになる
代わりにやるべきこと:
・Storybook でコンポーネントを視覚的に確認
・デザインが固まったらスナップショットテストを追加
・インタラクションロジック(クリック→状態変更)はTDDで書く
UIの「見た目」はTDDの対象外だが、UIの「振る舞い」はTDDの対象だ。この区別が重要だ。
// ❌ TDDが合わない:見た目のテスト
test('ボタンの色が#3B82F6である', () => {
render(<CancelButton />)
expect(screen.getByRole('button')).toHaveStyle({ color: '#3B82F6' })
})
// ✅ TDDが合う:振る舞いのテスト
test('キャンセルボタンをクリックすると確認ダイアログが表示される', () => {
render(<CancelButton onCancel={vi.fn()} />)
fireEvent.click(screen.getByRole('button', { name: 'キャンセル' }))
expect(screen.getByText('本当にキャンセルしますか?')).toBeVisible()
})
2. プロトタイプ / スパイク
場面:技術的な実現可能性を探っている段階
なぜTDDが合わないか:
・コードの90%を捨てる前提
・「動くか動かないか」だけが知りたい
・テストを書く時間が探索に使えない
代わりにやるべきこと:
・スパイク(探索的コーディング)で技術を検証
・検証完了後、得た知見をもとに本実装をTDDで書く
・スパイクコードは捨てる(流用しない)
Kent Beck 自身がこう述べている:「スパイクコードを書くのは全く問題ない。問題なのは、スパイクコードをそのまま本番に入れることだ」
3. 外部APIとの初回連携
場面:初めて使う外部API(決済、地図、認証)との接続を探っている段階
なぜTDDが合わないか:
・APIの挙動が分からない段階では、テストの期待値を書けない
・APIのレスポンス構造を理解するのが先
・認証フロー・リトライ・タイムアウトの挙動は試してみないと分からない
代わりにやるべきこと:
・まず REPL / スクリプトでAPIを叩いて挙動を把握する
・APIの境界にインターフェースを定義する(Ch.7の手法)
・インターフェースの内側(ビジネスロジック)はTDDで書く
・インターフェースの外側(API連携)は統合テストで検証する
4. インフラ・設定ファイル
場面:Docker Compose、Terraform、CI/CDパイプラインの設定
なぜTDDが合わないか:
・実行環境がローカルと異なる
・フィードバックループが長い(デプロイしないと分からない)
・「テスト」の定義が曖昧
代わりにやるべきこと:
・Infrastructure as Code のバリデーション(terraform validate)
・ステージング環境での統合テスト
・ヘルスチェック・スモークテスト
5. データマイグレーション
場面:既存DBのスキーマ変更、大量データの移行
なぜTDDが合わないか:
・テストデータと本番データの構造が異なる
・マイグレーション自体が一回限りの操作
・ロールバック戦略の検証はTDDでは困難
代わりにやるべきこと:
・ステージング環境でリハーサル
・ロールバック手順の事前準備
・マイグレーション後のバリデーションスクリプト
判断フレームワーク:TDD適用マトリクス
「使う / 使わない」の判断を体系化する。
quadrantChart
title TDD適用の判断マトリクス
x-axis "入出力の明確さ 低" --> "入出力の明確さ 高"
y-axis "変更頻度 低" --> "変更頻度 高"
quadrant-1 "TDD最適"
quadrant-2 "探索後にTDD"
quadrant-3 "TDD不要"
quadrant-4 "統合テスト"
"ドメインロジック": [0.85, 0.80]
"バリデーション": [0.90, 0.50]
"API ハンドラー": [0.75, 0.65]
"UIの見た目": [0.20, 0.85]
"UIの振る舞い": [0.70, 0.70]
"インフラ設定": [0.30, 0.20]
"プロトタイプ": [0.25, 0.90]
"データ移行": [0.50, 0.15]
"外部API連携": [0.40, 0.45]
| 象限 | 戦略 | 例 |
|---|---|---|
| TDD最適(右上) | 最初からTDDで開発 | ドメインロジック、バリデーション |
| 探索後にTDD(左上) | スパイクで探索→知見を得てからTDD | UI、プロトタイプ |
| 統合テスト(右下) | TDDではなく統合テストで保護 | インフラ、外部API |
| TDD不要(左下) | テスト不要、または別の品質保証手段 | 一回限りのスクリプト |
「部分的TDD」という選択肢
全てのコードにTDDを適用する必要はない。同じプロジェクト内でも、コードの性質に応じてTDDの適用度を変えるのが現実的だ。
SaaS課金管理システムでのTDD適用度:
██████████ 100% TDD
│ Money(金額計算)
│ Subscription(状態遷移)
│ ビジネスルール(割引、プロレーション)
│
████████░░ 80% TDD
│ ユースケース(CancelSubscription等)
│ API リクエストハンドラー
│
█████░░░░░ 50% TDD
│ UIコンポーネントの振る舞い
│ メール本文の生成
│
██░░░░░░░░ 20% TDD
│ 外部決済API連携
│ Webhook受信
│
░░░░░░░░░░ 0% TDD(統合テスト等で代替)
│ DBマイグレーション
│ Docker/CI設定
│ 管理画面のレイアウト
TDDを導入するときの段階的アプローチ
チームにTDDを導入するとき、いきなり全てにTDDを適用しようとすると失敗する。
段階的導入のステップ:
ステップ1:バグ修正からTDDを始める
・バグが報告されたら、まずバグを再現するテストを書く
・テストが Red になることを確認する
・バグを修正してテストを Green にする
→ 「既存のバグ」は最もテストを書きやすい題材
ステップ2:新機能の核心部分にTDDを適用する
・新しいビジネスルール、計算ロジックをTDDで書く
・UI、設定、外部連携は従来通り
ステップ3:テストしにくいコードのリファクタリング
・テストが書きにくいコードを発見したら、Ch.7-8の技法で構造を変える
・インターフェースの導入、依存の注入
最も重要なのはステップ1だ。バグ修正TDDは、TDDの価値を最も低リスクで体験できる方法だ。「このバグは二度と発生しない」という確信をテストが与えてくれる。
DHH の批判を再考する
Ch.2 で触れた DHH の「TDD is Dead」論を、ここまでの知識で再評価する。
DHH の批判(2014):
1. ユニットテスト駆動は不自然なコード分離を強制する
2. 「テストは高速でなければ」が結合テストの軽視を生む
3. テストファーストへの信仰がシステムテストの忘却を招く
2026年の評価:
1. → 部分的に正しい。Over-Mocking(AP-05)は実在する問題。
ただし、Fake ベースのテスト(Ch.7)はこの問題を回避できる。
2. → 部分的に正しい。テストピラミッドの底辺(ユニットテスト)だけでは不十分。
ただし、TDDの本質は「高速フィードバック」であり、テストの種類は手段。
3. → 正しい。TDDだけでは品質は担保できない。
E2Eテスト、負荷テスト、手動探索テストは別途必要。
DHH の批判は「教条的TDD」への批判としては正しい。しかし「TDD自体の否定」としては行き過ぎている。TDD は道具であり、使いどころを選ぶべきだ──これが2026年のコンセンサスだ。
本章のまとめ
| 要点 | 内容 |
|---|---|
| TDDが有効な領域 | 入出力が明確、正解が定義できるビジネスロジック |
| TDDが逆効果な領域 | UI探索、プロトタイプ、初回API連携、インフラ設定 |
| 判断軸 | 「入出力の明確さ」×「変更頻度」の2軸マトリクス |
| 部分的TDD | 同じプロジェクト内でもコードの性質に応じて適用度を変える |
| 導入戦略 | バグ修正TDD → 新機能の核心部分 → テストしにくいコードのリファクタリング |
次章では、2026年の最もホットなトピックに踏み込む。AI がコードを書く時代に、TDD はどう変わるのか。