Use Cases

TDDとClaude Codeの相性が最高な理由:テスト駆動開発の実践ガイド

Claude Codeでテスト駆動開発(TDD)を実践する方法を解説。テストファーストの開発フローとClaude Codeの組み合わせで品質と速度を両立します。

TDD × Claude Code の威力

テスト駆動開発(TDD)は「テストを書く → 失敗を確認 → 実装する → テストを通す → リファクタリング」のサイクルを繰り返す手法です。Claude Codeはこのサイクルをすべて自律的に回せるため、TDDの実践ハードルを大幅に下げてくれます。

Red-Green-Refactorをワンショットで

TDDの基本サイクルをClaude Codeに一括で任せる方法です。

> 以下の仕様でユーザー登録機能を実装して。TDDで進めて。
> - メールアドレスのバリデーション
> - パスワードは8文字以上、大小英字+数字を含む
> - 同一メールアドレスの重複登録を防止
>
> 1. テストを先に書く
> 2. テストが失敗することを確認
> 3. 実装してテストを通す
> 4. リファクタリングする

Claude Codeが生成するテスト例

import { describe, it, expect } from "vitest";
import { registerUser, ValidationError } from "./auth";

describe("registerUser", () => {
  it("正常なメールとパスワードで登録できる", async () => {
    const result = await registerUser({
      email: "[email protected]",
      password: "Passw0rd",
    });
    expect(result.success).toBe(true);
    expect(result.user.email).toBe("[email protected]");
  });

  it("無効なメールアドレスでエラーになる", async () => {
    await expect(
      registerUser({ email: "invalid", password: "Passw0rd" })
    ).rejects.toThrow(ValidationError);
  });

  it("短いパスワードでエラーになる", async () => {
    await expect(
      registerUser({ email: "[email protected]", password: "Pass1" })
    ).rejects.toThrow("8文字以上");
  });

  it("数字を含まないパスワードでエラーになる", async () => {
    await expect(
      registerUser({ email: "[email protected]", password: "Password" })
    ).rejects.toThrow("数字を含む");
  });

  it("重複メールアドレスでエラーになる", async () => {
    await registerUser({
      email: "[email protected]",
      password: "Passw0rd",
    });
    await expect(
      registerUser({ email: "[email protected]", password: "Passw0rd" })
    ).rejects.toThrow("既に登録されています");
  });
});

テストファーストのプロンプトパターン

パターン1:仕様からテストを生成

> 以下のAPI仕様書に基づいてテストを先に書いて。
> 実装はまだしないで。

テストだけ先に書かせてレビューし、問題なければ実装に進む2段階アプローチです。

パターン2:既存コードにテストを追加してからリファクタリング

> src/utils/formatter.ts にテストがない。
> まず現在の振る舞いを保証するテストを書いてから、
> コードをリファクタリングして。

このパターンはレガシーコードの改善に有効です。詳しくはレガシーコード改善ガイドを参照してください。

パターン3:境界値テストの自動生成

> calculatePrice 関数の境界値テストを網羅的に作成して。
> 0、負数、最大値、小数点以下の丸めもカバーして。
describe("calculatePrice", () => {
  it("数量0で合計0を返す", () => {
    expect(calculatePrice(100, 0)).toBe(0);
  });

  it("負の数量でエラーを投げる", () => {
    expect(() => calculatePrice(100, -1)).toThrow();
  });

  it("小数点以下を四捨五入する", () => {
    expect(calculatePrice(33, 3)).toBe(99);
  });

  it("大量注文で割引が適用される", () => {
    expect(calculatePrice(100, 1000)).toBe(90000); // 10%割引
  });
});

フックでTDDサイクルを自動化

Claude Codeのフック機能を使うと、ファイル変更のたびにテストを自動実行できます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "command": "npx vitest related \"$CLAUDE_FILE_PATH\" --run 2>&1 | tail -20"
      }
    ]
  }
}

フック機能の詳細はフック機能ガイドで解説しています。

CLAUDE.mdでTDDルールを強制

プロジェクトのCLAUDE.mdにルールを書いておくことで、Claude Codeが常にTDDスタイルで開発を進めます。

## 開発スタイル
- 新機能は必ずTDDで実装すること
- テストを先に書き、失敗を確認してから実装
- テストカバレッジ80%以上を維持
- テストファイルは __tests__ ディレクトリに配置

テスト戦略との組み合わせ

TDDで単体テストを書くだけでなく、統合テストやE2Eテストも含めた総合的なテスト戦略を構築することが重要です。テスト全体の設計についてはテスト戦略完全ガイドで解説しています。

まとめ

Claude CodeとTDDの組み合わせは、テストを書く面倒さを解消しながらコード品質を維持する強力な手法です。まずは小さな関数からTDDスタイルで試してみてください。

テストの書き方や設定について詳しくはAnthropic公式ドキュメントVitest公式サイトを参照してください。

#Claude Code #TDD #テスト駆動開発 #テスト #品質管理