テスト駆動開発

テストを先に書き、テストが通るコードを実装し、リファクタリングするサイクルで開発を進める手法

テスト開発手法

テスト駆動開発とは

テスト駆動開発 (Test-Driven Development, TDD) は、Kent Beck が 2002 年に体系化した開発手法で、Red → Green → Refactor の 3 ステップを繰り返してコードを書く。「テストを先に書く」ことで、設計の品質とテストカバレッジを同時に向上させる。

Red → Green → Refactor

1. Red:      失敗するテストを書く
2. Green:    テストが通る最小限のコードを書く
3. Refactor: コードを整理する (テストは通ったまま)
→ 1 に戻る

実践例

// Step 1: Red - 失敗するテストを書く
test('10,000円以上で10%割引', () => {
  expect(calculateDiscount(15000)).toBe(1500);
});
// → ReferenceError: calculateDiscount is not defined

// Step 2: Green - テストが通る最小限のコードを書く
function calculateDiscount(total: number): number {
  if (total >= 10000) return total * 0.1;
  return 0;
}
// → ✅ テスト通過

// Step 3: Refactor - コードを整理
const DISCOUNT_THRESHOLD = 10000;
const DISCOUNT_RATE = 0.1;

function calculateDiscount(total: number): number {
  if (total >= DISCOUNT_THRESHOLD) return total * DISCOUNT_RATE;
  return 0;
}
// → ✅ テスト通過 (リファクタリング後も)

// Step 1 (次のサイクル): 新しいテストを追加
test('会員は15%割引', () => {
  expect(calculateDiscount(15000, true)).toBe(2250);
});
// → Red → Green → Refactor ...

TDD のメリット

  • テストカバレッジが自然に高くなる (テストを先に書くため)
  • 設計が改善される (テストしやすい = 疎結合な設計)
  • リファクタリングが安全にできる (テストが回帰を検出)
  • 過剰な実装を防ぐ (テストが通る最小限のコードだけ書く)

TDD が難しいケース

ケース 理由 代替アプローチ
UI のスタイリング 見た目のテストが困難 ビジュアルリグレッションテスト
外部 API との連携 モックの設定が複雑 結合テストで補完
探索的な開発 仕様が不明確 スパイクで仕様を固めてから TDD
レガシーコード テストが書きにくい構造 まずリファクタリングしてテスタブルに

TDD と BDD の違い

手法 テストの記述 焦点
TDD expect(calculateDiscount(15000)).toBe(1500) 実装の正しさ
BDD Given 15000円の注文 When 割引計算 Then 1500円 ビジネス要件の充足

BDD (Behavior-Driven Development) は TDD の発展形で、ビジネス要件をテストとして記述する。

Lambda での TDD

Lambda ハンドラーは純粋関数に近い (入力 → 出力) ため、TDD と相性が良い。

// テストを先に書く
test('正常な注文リクエストで 201 を返す', async () => {
  const event = createApiEvent({ body: JSON.stringify({ item: 'Book', qty: 1 }) });
  const result = await handler(event);
  expect(result.statusCode).toBe(201);
});

実務での活用方法は関連書籍にも詳しい。

関連用語