テスト駆動AI開発
TDDとAI開発の相乗効果:Kent Beckも推奨
“AI is not replacing TDD, it’s accelerating it.”
AIはTDDを置き換えるのではなく、加速させます。
Kent Beck(TDDの父)も、AIエージェントと協働する際にTDDを「スーパーパワー」と表現しています。
なぜTDD×AIが効果的か
8.2.1 テストが「プロンプト」になる
“By writing a test before you write any code, you are essentially ‘prompting’ the AI code generator with exactly the functionality you want.”
テストを先に書くことで:
- AIに正確な機能仕様を伝える
- 期待する動作を明示
- 即座にフィードバックを得る
8.2.2 人間が主導権を保つ
テストを通じて:
- 期待する結果を定義
- AIの貢献をコントロール
- 品質を担保
TDD×AIワークフロー
8.3.1 基本フロー
[1. 要件を定義(人間)]
↓
[2. テストケースを設計(人間 + AI)]
↓
[3. テストコードを生成(AI)]
↓
[4. テストをレビュー・修正(人間)]
↓
[5. 実装を生成(AI)]
↓
[6. テストを実行(自動)]
↓
[7. 失敗したら修正(AI + 人間)]
↓
[8. リファクタリング(AI + 人間)]
8.3.2 Red-Green-Refactor with AI
Red(失敗するテストを書く):
プロンプト:
「以下の要件でテストを書いてください。
テストは現時点で失敗するはずです。
要件:
- ユーザーのメールアドレスをバリデーション
- 有効な形式なら true を返す
- 無効な形式なら false を返す」
Green(最小限の実装):
プロンプト:
「このテストが通る最小限の実装を書いてください。
過度な最適化や機能追加は不要です。」
Refactor(改善):
プロンプト:
「テストが通ることを維持しながら、
このコードをリファクタリングしてください。」
AIによるテスト生成
8.4.1 テストケース提案
## コピー可能なAI開発プロンプト例
以下の関数に対するテストケースを提案してください:
```typescript
function calculateDiscount(
price: number,
membershipLevel: 'bronze' | 'silver' | 'gold'
): number
カバーすべきケース:
- 正常系(各メンバーシップレベル)
- 異常系(不正な価格、不正なレベル)
- エッジケース(0円、マイナス、非常に大きな値)
### 8.4.2 テストコード生成
```markdown
## コピー可能なAI開発プロンプト例
Jestを使用してテストを実装してください。
要件:
- Arrange-Act-Assertパターン
- 各テストは独立
- モックは最小限
- 説明的なテスト名
```typescript
// テスト対象
function calculateDiscount(
price: number,
membershipLevel: 'bronze' | 'silver' | 'gold'
): number {
const discountRates = { bronze: 0.05, silver: 0.10, gold: 0.15 }
return price * (1 - discountRates[membershipLevel])
}
### 8.4.3 生成されたテストの例
```typescript
describe('calculateDiscount', () => {
describe('正常系', () => {
it('bronzeメンバーは5%割引', () => {
// Arrange
const price = 1000
const level = 'bronze'
// Act
const result = calculateDiscount(price, level)
// Assert
expect(result).toBe(950)
})
it('silverメンバーは10%割引', () => {
expect(calculateDiscount(1000, 'silver')).toBe(900)
})
it('goldメンバーは15%割引', () => {
expect(calculateDiscount(1000, 'gold')).toBe(850)
})
})
describe('エッジケース', () => {
it('価格が0の場合は0を返す', () => {
expect(calculateDiscount(0, 'gold')).toBe(0)
})
it('小数点を含む価格を正しく計算', () => {
expect(calculateDiscount(99.99, 'bronze')).toBeCloseTo(94.99)
})
})
})
AIテスト生成の注意点
8.5.1 よくある問題
実装の詳細に依存:
// ❌ AIが生成しがちな問題のあるテスト
test('should call internal method', () => {
const spy = jest.spyOn(obj, '_privateMethod')
obj.publicMethod()
expect(spy).toHaveBeenCalled()
})
// ✅ 振る舞いをテスト
test('should return processed result', () => {
const result = obj.publicMethod()
expect(result).toEqual(expectedOutput)
})
過度なモック:
// ❌ すべてをモック
jest.mock('./database')
jest.mock('./logger')
jest.mock('./config')
jest.mock('./utils')
// ✅ 必要最小限のモック
jest.mock('./database') // 外部依存のみ
8.5.2 レビューのチェックリスト
## AI生成テストのレビュー
- [ ] 意図した動作をテストしているか
- [ ] 実装の詳細ではなく振る舞いをテストしているか
- [ ] モックは最小限か
- [ ] テスト名は説明的か
- [ ] エッジケースがカバーされているか
- [ ] 偽陽性/偽陰性の可能性はないか
Test-Driven Generation(TDG)
8.6.1 新しいアプローチ
TDG(テスト駆動生成)は、TDDをAI時代に進化させたアプローチです。
従来のTDD:
人間がテストを書く → 人間が実装
TDG:
人間が仕様を定義 → AIがテストと実装を生成
→ 人間がレビュー
8.6.2 TDGワークフロー
1. 人間が高レベルの仕様を記述
「ユーザー登録機能。メール重複チェック必須」
2. AIがテストケースを提案
「メール重複時にエラー」「空メールでエラー」...
3. 人間がテストケースをレビュー・承認
4. AIがテストコードを生成
5. 人間がテストをレビュー・修正
6. AIが実装を生成
7. テスト実行で検証
8. リファクタリング
実践例
8.7.1 新機能開発
Step 1: 仕様を定義
「ユーザーのパスワードリセット機能を実装したい。
- メールアドレスを入力
- トークンを生成(24時間有効)
- リセットリンクをメール送信
- トークン検証後、新パスワードを設定」
Step 2: テストケースを依頼
「上記の仕様に対するテストケースを提案してください」
Step 3: テストコード生成
「Jestでテストを実装してください」
Step 4: レビューと修正
「このテストでは〇〇が考慮されていません。追加してください」
Step 5: 実装生成
「このテストが通る実装を書いてください」
Step 6: 実行と調整
今日から実践できるアクション
- 次の機能でテストを先に書く: AIにテストケースを提案させる
- AIに実装させる: テストが通ることを確認
- リファクタリング: テストを維持しながら改善
まとめ:重要ポイントの振り返り
- TDD×AI: AIはTDDを加速させる
- テストがプロンプト: 仕様を明確に伝える
- Red-Green-Refactor: AIと協働で実践
- テスト生成の注意: 振る舞いをテスト、実装詳細は避ける
- TDG: AI時代のTDD進化形
- 教訓:テストを先に書くことで、AIの出力品質が向上する