| name | tdd-cycle |
| description | t-wada式TDD(テスト駆動開発)でRED→GREEN→REFACTORサイクルを使ってテストファーストで開発を進めるスキル。
ユーザーが「TDDで実装して」「テストファーストで」「t-wada式で」「REDから始めて」「失敗するテストを先に」と言った場合は必ずこのスキルを使うこと。
FizzBuzz、スタック、認証、バリデーションなど、あらゆる実装タスクにTDDアプローチを適用できる。
ユーザーが実装を始めようとしているときは積極的にTDDを提案すること。
|
t-wada式TDDスキル
哲学
t-wada式TDDの核心は「テストが仕様書」という考え方。コードを書く前にテストを書くことで、
設計を明確にし、過剰実装を防ぎ、リファクタの安全網を得る。
Fake it till you make it: GREEN フェーズでは恥ずかしいほど単純なコード(ハードコードも可)で構わない。
三角測量(Triangulation)で徐々に汎化させる。
サイクル
RED → GREEN → REFACTOR → RED → ...
↑___________________________|
各フェーズで必ずテストを実行し、期待通りの色(RED/GREEN)を確認してから次へ進む。
Phase 1: RED(失敗するテストを書く)
ステップ
-
要件を1つ選ぶ: 最も小さく具体的な振る舞いを1つ選ぶ。複数の要件を一度にテストしない。
-
テストを書く: まだ存在しない関数・クラスを呼び出すテストを書く。
- テスト名は「〜のとき〜になる」という仕様の文章で書く
- AAAパターン(Arrange / Act / Assert)を意識する
- 1テスト1アサーションを基本とする
-
テストを実行して失敗を確認する: 必ずテストを実行し、期待通りに失敗することを確認する。
ImportError や NameError で失敗するのは正常(まだ実装がないため)
- アサーションエラーで失敗するのが理想的な RED
- もしテストが「偶然パス」したら、テストが間違っている
-
ユーザーに RED を報告する:
🔴 RED
テスト: [テスト名]
失敗理由: [エラーメッセージ]
次: GREENフェーズでテストをパスさせます
-
skill-usage.json に RED ログを追記する:
.claude/orchestrate/skill-usage.json が存在する場合、invocations 配列に追記する:
{
"id": "inv-XXX",
"timestamp": "[ISO8601]",
"skill": "tdd-cycle",
"caller": "tdd-guide",
"feature_id": "[feature_id(不明なら 'unknown')]",
"phase": "RED",
"test_name": "[テスト名]",
"result": "FAIL",
"error": "[失敗理由]"
}
ファイルが存在しない場合はスキップする(build-cycle の LOAD フェーズが初期化する)。
Phase 2: GREEN(最小限のコードでテストをパスさせる)
鉄則
テストをパスさせるための最小限のコードだけを書く。それ以上書かない。
⚠️ Context anxiety 対策: 全テストが GREEN になるまで「完了」とユーザーに報告してはならない。
コンテキストが長くなった場合は現在のサイクル状態(どのテストがRED/GREEN/未着手か)を
tdd-progress.md に書き出してから /compact すること。
「最小限」とは本当に最小限。最初は定数を返すだけでよい。「このコードは恥ずかしい」と感じるくらいがちょうどよい。
それが Fake it の精神。恥ずかしいコードを書くことで、次のテストが追加の動機となって自然に汎化される。
⚠️ よくある罠: 先に「完成形」を書いてしまう
| 状況 | ❌ NG(完成形) | ✅ OK(Fake it) |
|---|
| 数値返し | if n%15==0: return "FizzBuzz" ... | return "1" |
| boolean返し | return len(self._items)==0 | return True |
| オブジェクト操作 | self._items.append(item); return sum(...) | return 0 |
最初のテストが1つしかないなら、その1つを通す最もシンプルなコードでよい。次のテスト(三角測量)が追加されたとき初めて汎化を強制される。
完成形を先に書くのは、見た目はTDDでも三角測量が機能しない。1テスト→最小実装→次のテストで汎化、を厳守する。
ステップ
-
ベタ書きから始める(Fake it): ハードコードした値を返すだけのコードを書く。
テストが1つしかないなら、その1つをパスさせる最もシンプルなコードでよい。
-
テストを実行してパスを確認する: 必ずテストを実行し、GREEN になったことを確認する。
- まだ他のケースは考えなくてよい
- コードが「汚い」のは問題ない。REFACTORフェーズで直す
-
ユーザーに GREEN を報告する:
✅ GREEN
テスト: [テスト名]
実装: [書いたコードの概要]
次: REFACTORするか、次のREDに進みます
-
skill-usage.json に GREEN ログを追記する:
.claude/orchestrate/skill-usage.json が存在する場合、invocations 配列に追記する:
{
"id": "inv-XXX",
"timestamp": "[ISO8601]",
"skill": "tdd-cycle",
"caller": "tdd-guide",
"feature_id": "[feature_id]",
"phase": "GREEN",
"test_name": "[テスト名]",
"result": "PASS",
"implementation": "[実装コードの概要(1行)]"
}
Phase 3: REFACTOR(テストを通したままクリーンにする)
ステップ
-
リファクタの必要性を判断する: GREEN になったコードを見て、以下を確認する。
- 重複している箇所はあるか?
- マジックナンバー・マジック文字列はあるか?
- 名前は意図を表しているか?
-
fix-debt と flaky-test-detect を並列実行する: GREENになったファイルに対して両スキルを同時に呼び出す。
| スキル | 対象 | 目的 |
|---|
fix-debt | プロダクションコード | 条件分岐・カプセル化・関心の分離の3基準で技術的負債を検出・自動修正 |
flaky-test-detect | テストコード | フレーキーテストを検出し、TIMING/GLOBAL_STATE等のパターンに分類して修正 |
- 両スキルの修正は独立しているため並列実行が安全
fix-debt はリグレッションが出た修正を自動リバート
flaky-test-detect は検出したフレーキーテストを修正し、再度テストを実行して安定を確認
-
小さく変更し、その都度テストを実行する: fix-debt で対応できない軽微な問題(命名・マジックナンバー等)は手動で修正する。1変更ごとにテストを実行して GREEN を保つ。
-
テストコード自体もリファクタ対象: プロダクションコードと同様に、テストコードも整理する。
-
ユーザーに REFACTOR 結果を報告する:
🔵 REFACTOR完了
変更内容: [リファクタの概要(fix-debtの適用内容を含む)]
テスト: 全パス
次: 次の要件のREDフェーズへ
-
skill-usage.json に REFACTOR ログを追記する:
.claude/orchestrate/skill-usage.json が存在する場合、invocations 配列に追記し updated_at も更新する:
{
"id": "inv-XXX",
"timestamp": "[ISO8601]",
"skill": "tdd-cycle",
"caller": "tdd-guide",
"feature_id": "[feature_id]",
"phase": "REFACTOR",
"changes": "[fix-debt・flaky-test-detect の適用内容(1行)]",
"result": "PASS"
}
三角測量(Triangulation)
Fake itで汎化しきれていない場合は、coverage-gap スキルを使って追加すべきテストを特定し、汎化を強制する。
完了基準: ユーザーが指定した全ての振る舞いのテストが GREEN になり、各フェーズの REFACTOR が完了したら TDD セッション完了とする。coverage-gap による三角測量は Fake it で汎化しきれていないと判断した場合にのみ使用し、必須ではない。
Triangulation ワークフロー
-
coverage-gap で未カバー箇所を特定する: 現在実装したファイルを対象に coverage-gap スキルを呼び出す。
- カバレッジデータから「まだテストされていない分岐・ケース」を抽出
- Critical/High 優先度のギャップを三角測量の次のテストとして使う
-
追加テストを RED で確認する: coverage-gap が提案したテストを RED フェーズとして追加する。
- 新しいテストが失敗する(RED)ことを確認
- 失敗しない場合は Fake it が偶然汎化していた証拠なので、さらに別のテストを探す
-
汎化した実装で GREEN にする: ハードコードを取り除き、全テストが通る実装に更新する。
-
サイクルを繰り返す: coverage-gap を再実行して残った未カバー箇所がなくなるまで繰り返す。
例
coverage-gap → RED → GREEN → REFACTOR → coverage-gap の繰り返しで、
カバレッジデータが三角測量の「何を次にテストすべきか」を客観的に示す。
言語別テスト実行コマンド
| 言語 | フレームワーク | コマンド |
|---|
| Python | pytest | pytest -v [test_file] |
| TypeScript/JS | vitest | npx vitest run [test_file] |
| TypeScript/JS | jest | npx jest [test_file] |
| Go | testing | go test ./... |
| Rust | cargo test | cargo test |
テスト実行コマンドが不明な場合はユーザーに確認する。
TDDを積極的に提案する(ユーザーが明示していない場合)
ユーザーが「〇〇を実装したい」「〇〇を書こうと思ってる」と言ったとき、TDDを提案する。
提案テンプレート:
〇〇の実装、よければt-wada式TDD(テスト駆動開発)で進めてみませんか?
最初に「最も単純なテスト1つ」を書くことで仕様が明確になり、過剰実装を防げます。
例えば最初のテストはこんなイメージです:
def test_[最も簡単なケース]():
[最小の準備]
assert [期待する結果]
これをREDから始めてよいですか?テストフレームワークはpytestでよいですか?
ユーザーが同意したらセッションの始め方へ進む。断られたら通常の実装支援に切り替える。
セッションの始め方(TDD合意後)
- 最初の小さな一歩を決める: 最も小さく具体的な振る舞いを1つ選ぶ
- 「最初にテストしたいのは、どんな一番簡単なケースですか?」
- 「ショッピングカートなら『1商品追加して合計を取る』が最初の一歩では?」
- テストファイルの場所を確認する: 既存のテスト構成に合わせる
- REDフェーズを開始する
ユーザーが「TDDで実装して」と漠然と言ったときは、「まず最も単純な入力と期待する出力を1つ教えてください」と聞くと、最初のテストが具体化される。
アンチパターン(避けるべきこと)
- テストより先に実装を書く: GREENを先に確保するのはTDDではない
- 複数の要件を一度にテストする: 1テスト1振る舞い
- REDを確認せずにGREENへ進む: 常にテストを実行して色を確認する
- 過剰実装(YAGNI違反): "You Ain't Gonna Need It" — 今必要なものだけ実装する
- リファクタ中にテストを壊す: REFACTORはGREENを保ちながら行う