目次を表示する

TDD実践ガイド 2026

TDDの限界と「使わない判断」── 適用すべき場所、してはいけない場所

本章の方針

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(左上)スパイクで探索→知見を得てからTDDUI、プロトタイプ
統合テスト(右下)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 はどう変わるのか