name: tdd description: t_wadaのTDDプラクティスに従ってRed→Green→Refactorサイクルを実行する disable-model-invocation: true argument-hint: "[実装したい機能の説明 or ファイルパス]" allowed-tools: - Bash(pnpm run test:) - Bash(pnpm exec vitest) - Read - Edit - Write - Grep - Glob
t_wada式 TDD サイクル
t_wadaのTDDプラクティスに忠実に従い、Red→Green→Refactorのサイクルを回す。
基本原則
- テストが先、実装が後。例外なし
- 動作するきれいなコードがゴール
- 一度に1つのテストだけに集中する
- テストコードも本番コードと同じ品質を保つ
手順
Phase 0: TODOリスト作成
$ARGUMENTSの内容を分析し、実装に必要なテストケースをTODOリストとして洗い出す。
- 実装したい機能を小さなステップに分解する
- 各ステップをテストケース名(日本語)として書き出す
- 最も簡単で、最も重要なテストケースを最初に選ぶ
- TODOリストをユーザに提示し、合意を得る
TODOリスト例:
- [ ] 空文字列を渡すとバリデーションエラーになること
- [ ] 有効な値を渡すとオブジェクトが生成されること
- [ ] 境界値で正しく動作すること
Phase 1: 🔴 Red(失敗するテストを書く)
- TODOリストから1つだけテストケースを選ぶ
- テストファイルを作成/編集する
- 配置: ソースファイルと同じディレクトリにco-locate(
*.test.ts) - テストケース名は日本語で記述する
describeで正常系・異常系を分類する
- 配置: ソースファイルと同じディレクトリにco-locate(
- テストを実行し、失敗することを確認する
pnpm run test:domain、pnpm run test:server、pnpm run test:clientのうち適切なものを使用- 失敗理由が意図通りか確認する(コンパイルエラーではなくアサーション失敗が理想)
- 失敗を確認できたら次のフェーズへ進む
重要: このフェーズではプロダクションコードを書かない。テストだけを書く。
Phase 2: 🟢 Green(最小限の実装で通す)
以下の3つの戦略を段階的に使い分ける:
戦略1: 仮実装(Fake It)
- テストを通す最も簡単な実装を書く
- ハードコードした値を返すだけでもOK
- 目的: まず「グリーン」の状態を作る
// 例: 最初はハードコードで通す
getDisplayName(): string {
return "太郎"
}
戦略2: 三角測量(Triangulate)
- 1つ目のテストが仮実装で通ったら、2つ目のテストケースを追加する
- 2つのテストを同時に通すには、仮実装では不十分になる
- これにより汎用的な実装へ進化させる
// 2つのテストケースが存在するので、ハードコードでは通らない
// → 汎用的な実装に書き換える
getDisplayName(): string {
return this.name
}
戦略3: 明白な実装(Obvious Implementation)
- 実装方法が明らかな場合は、直接正しい実装を書く
- ただし、テストが失敗したら仮実装に戻る
重要: テストを実行し、グリーンになったことを確認する。
Phase 3: 🔵 Refactor(リファクタリング)
テストがグリーンの状態を維持しながら、コードを改善する。
- プロダクションコードの重複を除去する
- テストコードの重複を除去する(テストコードもリファクタリング対象)
- 命名を改善する
- リファクタリング後、テストを実行してグリーンを確認する
- 必要に応じて
pnpm run lintを実行する
重要: リファクタリング中に新しい機能を追加しない。振る舞いを変えずに構造を改善する。
Phase 4: サイクルを繰り返す
- TODOリストの完了したテストケースにチェックを入れる
- 次のテストケースを選び、Phase 1に戻る
- 実装中に気づいた新しいテストケースはTODOリストに追加する
- 全てのTODOが完了するまで繰り返す
プロジェクト固有のルール
- テストフレームワーク: Vitest(globals有効、import不要)
- テストファイル命名:
*.test.ts/*.test.tsx - テスト配置: ソースファイルと同じディレクトリにco-locate
- モック:
vitest-mock-extendedのmockDeepを使用 - テストデータ:
@faker-js/fakerで生成 - バリデーション: Zodスキーマの
safeParse()でテスト - 結果型:
@yuukihayashi0510/coreのsuccess/failureパターン - パラメータ化テスト:
it.each()を活用 - インポート:
@/*の絶対パスを使用 - テストケース名: 日本語で記述
テストコマンド
対象に応じて適切なコマンドを使う:
pnpm run test:domain- ドメイン層pnpm run test:server- API/サーバー層pnpm run test:client- フロントエンド層pnpm run test:common- 共通層
各フェーズの確認ポイント
| フェーズ | 確認すること |
|---|---|
| Red | テストが意図した理由で失敗しているか |
| Green | 全てのテストがパスしているか(新旧含む) |
| Refactor | リファクタリング後も全テストがパスしているか |
やってはいけないこと
- 失敗するテストなしにプロダクションコードを書く
- 一度に複数のテストケースを実装する
- Redフェーズでプロダクションコードを書く
- Refactorフェーズで新機能を追加する
- テストを実行せずに次のフェーズに進む
- テストがRedのままRefactorに入る