Claude Code攻略#Claude Code#TDD#テスト
テスト駆動開発 with Claude Code【TDD実践ガイド】
Claude Codeを使ったテスト駆動開発(TDD)の実践方法。Red-Green-Refactorサイクルを効率的に回すテクニック。
TDDとは
テスト駆動開発(TDD: Test-Driven Development)は:
- Red: 失敗するテストを書く
- Green: テストが通る最小限のコードを書く
- Refactor: コードを改善する
このサイクルを繰り返す開発手法です。
なぜClaude Code × TDDなのか
Claude Codeを使うと:
- テストコードの生成が速い
- 実装コードも自動生成
- リファクタリングも任せられる
人間が「何を作りたいか」を考え、Claudeが「テスト→実装→改善」を実行します。
TDDの実践フロー
STEP 1: テストファイルを作成
calcPrice関数をTDDで作りたい。
要件:
- 通常価格を返す
- 数量が10以上なら10%割引
- 数量が100以上なら20%割引
まず失敗するテストを書いて。
Claudeが生成するテスト:
// src/lib/pricing.test.ts
import { calcPrice } from './pricing'
describe('calcPrice', () => {
it('通常価格を返す(数量1)', () => {
expect(calcPrice(1000, 1)).toBe(1000)
})
it('数量が10以上なら10%割引', () => {
expect(calcPrice(1000, 10)).toBe(9000) // 1000 * 10 * 0.9
})
it('数量が100以上なら20%割引', () => {
expect(calcPrice(1000, 100)).toBe(80000) // 1000 * 100 * 0.8
})
})
STEP 2: テストを実行(Red)
テストを実行して。失敗することを確認。
結果:
FAIL src/lib/pricing.test.ts
calcPrice
✕ 通常価格を返す(数量1)
✕ 数量が10以上なら10%割引
✕ 数量が100以上なら20%割引
Test Suites: 1 failed
Tests: 3 failed
テストが失敗することを確認(関数がまだ存在しないため)。
STEP 3: 最小限の実装(Green)
テストが通る最小限のコードを書いて。
過度な設計は不要。まずテストを通すことだけ考えて。
Claudeが生成:
// src/lib/pricing.ts
export function calcPrice(unitPrice: number, quantity: number): number {
const total = unitPrice * quantity
if (quantity >= 100) {
return total * 0.8
}
if (quantity >= 10) {
return total * 0.9
}
return total
}
STEP 4: テスト実行(Green確認)
テストを実行して。
結果:
PASS src/lib/pricing.test.ts
calcPrice
✓ 通常価格を返す(数量1)
✓ 数量が10以上なら10%割引
✓ 数量が100以上なら20%割引
Test Suites: 1 passed
Tests: 3 passed
STEP 5: リファクタリング(Refactor)
テストが通った状態を維持しながら、コードを改善して。
- 定数の抽出
- 型の強化
- 可読性の向上
改善後:
// src/lib/pricing.ts
const DISCOUNT_TIERS = [
{ minQuantity: 100, discount: 0.2 },
{ minQuantity: 10, discount: 0.1 },
] as const
export function calcPrice(unitPrice: number, quantity: number): number {
const total = unitPrice * quantity
const tier = DISCOUNT_TIERS.find(t => quantity >= t.minQuantity)
return tier ? total * (1 - tier.discount) : total
}
リファクタリング後もテストが通ることを確認して。
実践例: ユーザー登録機能
要件定義
ユーザー登録機能をTDDで作りたい。
要件:
- メールアドレスは必須、形式チェック
- パスワードは8文字以上
- 同じメールアドレスは登録不可
- 登録成功でユーザーIDを返す
まず失敗するテストを書いて。
テストコード
// src/services/user.test.ts
import { registerUser } from './user'
describe('registerUser', () => {
beforeEach(() => {
// テスト用DBをリセット
})
describe('バリデーション', () => {
it('メールアドレスがない場合はエラー', async () => {
await expect(
registerUser({ email: '', password: 'password123' })
).rejects.toThrow('Email is required')
})
it('メールアドレスの形式が不正な場合はエラー', async () => {
await expect(
registerUser({ email: 'invalid', password: 'password123' })
).rejects.toThrow('Invalid email format')
})
it('パスワードが8文字未満の場合はエラー', async () => {
await expect(
registerUser({ email: 'test@example.com', password: 'short' })
).rejects.toThrow('Password must be at least 8 characters')
})
})
describe('登録処理', () => {
it('正常に登録できる', async () => {
const result = await registerUser({
email: 'test@example.com',
password: 'password123'
})
expect(result.userId).toBeDefined()
})
it('同じメールアドレスは登録不可', async () => {
await registerUser({
email: 'test@example.com',
password: 'password123'
})
await expect(
registerUser({
email: 'test@example.com',
password: 'password456'
})
).rejects.toThrow('Email already exists')
})
})
})
実装を依頼
このテストが通る実装を書いて。
TDDのコツ
コツ1: テストは具体的に
❌ 「ユーザー登録のテストを書いて」
✅ 「メールアドレスが空の場合にエラーを返すテストを書いて」
コツ2: 一度に1つのテスト
テストを1つずつ追加して、その都度実装を更新して。
コツ3: エッジケースを忘れずに
このメソッドのエッジケースを洗い出して、テストを追加して:
- 空の入力
- 境界値
- 異常な入力
コツ4: モックを活用
外部API呼び出しをモックしてテストを書いて。
テストの種類と使い分け
ユニットテスト
calcPrice関数の単体テストを書いて。
外部依存はモック。
統合テスト
API エンドポイント /api/users の統合テストを書いて。
実際のDBを使用(テスト用)。
E2Eテスト
ユーザー登録〜ログインまでのE2Eテストを書いて。
Playwrightを使用。
よくある質問
Q: どこまでテストを書くべき?
このプロジェクトで特に重要な部分を教えて。
その部分を優先的にテストしたい。
一般的な優先順位:
- ビジネスロジック(計算、判定)
- API エンドポイント
- 複雑な状態管理
- UIコンポーネント
Q: テストが多くてメンテが大変
このテストファイルをリファクタリングして:
- 重複を排除
- テストヘルパーを作成
- 可読性を向上
次のステップ
TDDの基本をマスターしました!
次は、より高度なデバッグテクニックを学びましょう。
まとめ
- TDD = Red → Green → Refactor のサイクル
- まず失敗するテストを書く
- テストが通る最小限のコードを実装
- テストが通った状態でリファクタリング
- Claudeにテスト生成→実装→改善を任せる
- 要件を明確にするほどテストの品質が上がる
参考文献・引用元
- [1]Claude Code Documentation- Anthropic