tdd
新機能追加、バグ修正、既存コード変更を RED → GREEN → REFACTOR のテスト駆動開発で実装する skill。コードの実装・修正を依頼されたときに使用する。
Files2
View on GitHubSKILL.mdskills/tdd/SKILL.md
GitHubTest-Driven Development
目的
変更を、小さな RED → GREEN → REFACTOR サイクルで進める。
テストでは、利用側から観測できる振る舞いを検証する。実装詳細は検証しない。 実装を大きく変更しても、外から見える振る舞いを保つ限り、テストが壊れにくい状態を維持する。
この skill の実行中は、判断可能な事項についてユーザー確認のために作業を止めない。 通常の判断で解決できない場合は、最も安全で既存設計に沿った選択肢を採用し、その判断を完了報告に記載する。
ただし、ユーザーと合意した内容を変える必要がある場合は、自分で選ばずに作業を止め、ユーザーに確認する。 合意した内容とは、ユーザーが依頼や会話で明示したか、提案を承認した事項を指す。 対象は、仕様・変更範囲・名前・型・signature・モジュールの分け方・前提とする。 実装の前に形や仕様の合意を求められている間は、合意を得るまで実装を始めない。 作業を止めるときは、終わったサイクル、失敗したままのテスト、途中の変更を報告する。
原則
振る舞いをテストする
テストは、安定したインターフェースまたは意味のある境界を通して振る舞いを検証する。
避ける対象は次のとおり。
- private メソッド
- 一時的な内部データ構造
- 内部関数の呼び出し順序
- 実装都合だけで存在する helper
- リファクタリングで簡単に変わる内部構造
内部モジュールを直接テストするときは、そのモジュールが独立した責務と安定した契約を持っていることを確認する。 この条件を満たさない内部実装は直接テストしない。
テストは仕様として読めるようにする
テスト名は、システムが保証する振る舞いを表現する。
テスト名に実装方法を書かない。
モックは外部境界の制御に限定する
内部実装の呼び出し回数、呼び出し順序、内部メソッドの利用有無を固定するための mock は使用しない。
fake / stub / mock は、外部境界を制御するために使用する。
mock は内部実装を固定せず、外部境界を制御して対象の振る舞いを安定して観測するために使う。
Subagents
調査や検証は subagent に委譲してよい。
ただし、同じ変更対象の RED → GREEN → REFACTOR サイクルは1つの実装担当が直列に進める。
以下は並列化してよい。
- 既存仕様の調査
- 関連コード・テストの探索
- 外部ドキュメントの確認
- TDD サイクル外の独立した検証
以下は並列化してはいけない。
- 同じ振る舞いに対する RED
- その GREEN 実装
- その REFACTOR
- 前のサイクルの知見を前提とする次の振る舞い
Anti-Pattern: Horizontal Slices
すべてのテストを書いてから、まとめて実装してはいけない。
RED:
test1
test2
test3
test4
GREEN:
impl1
impl2
impl3
impl4
複数の振る舞いを先にテストとして固定すると、実装を通して得られる知見を次のテストへ反映できない。
Correct Approach: Vertical TDD
1サイクルでは、1つの振る舞いだけを扱う。
RED
↓
GREEN
↓
REFACTOR
↓
次の振る舞い
複数の独立した振る舞いを1サイクルへまとめてはいけない。
1つの振る舞いを複数入力で検証するときは、複数 assertion または table-driven test を使用してよい。 この場合も、対象とする振る舞いは1つに限定する。
Workflow
1. 調査
コードを書く前に、対象範囲を確認する。
- 関連する
AGENTS.mdと repository instructions を読む - 既存コードを確認する
- 既存テストを確認する
- 既存の設計、命名、テストパターンを確認する
- 変更対象のインターフェースと振る舞いを特定する
要求や既存コードから合理的に判断できる事項について、ユーザー確認のために作業を止めない。
複数の解釈が成立するときは、次の順序で判断する。
- 既存テストが示す振る舞い
- 既存 API と呼び出し側が示す契約
- repository 内の設計パターン
- 最小差分で維持できる既存挙動
判断した場合は、完了報告に記載する。
2. 振る舞いの整理
実装手順ではなく、必要な振る舞いを整理する。
優先順位は次の順序とする。
- 主要な利用パス
- バグの再現条件
- 複雑なロジック
- 回帰リスクが高い境界
- 重要なエラー処理
テスト対象を増やす前に、現在の振る舞いを1つ選び、そのサイクルを完了する。
3. RED
次に実装する1つの振る舞いについてテストを書く。
その後、必ずテストを実行する。
RED とみなす条件は次のとおり。
- テストが失敗する
- その失敗原因が、今回追加しようとしている振る舞いが未実装であることに対応している
次は正しい RED に該当しない。
- syntax error
- import error
- fixture の不足
- test setup の失敗
- DB や依存サービスが起動していない
- 関係のない既存テストの失敗
意図しない理由で失敗した場合は、その原因を修正して同じテストを再実行する。 期待する理由で失敗するまで GREEN の実装へ進まない。
4. GREEN
RED を解消するための最小実装を書く。
次のルールに従う。
- 現在の振る舞いだけを満たす
- 将来必要になりそうな機能を先回りしない
- 不要な抽象化を追加しない
- 関係のないコードを変更しない
- 既存の設計やパターンを優先する
実装後、関連テストを実行して GREEN を確認する。
GREEN を確認せず、次の振る舞いへ進んではいけない。
5. REFACTOR
GREEN の直後に、必ず REFACTOR フェーズを実行する。
以下を1項目ずつ確認する。
- 重複がないか
- 命名が不明確でないか
- 責務が混在していないか
- 不要な複雑さがないか
- 不要な抽象化がないか
- 依存方向が不自然でないか
- API をより単純に保てないか
- テストが実装詳細に依存していないか
- GREEN のためだけの不自然なコードが残っていないか
改善点を検出した項目は、そのサイクル内で修正する。 ただし、合意した内容に関わる改善は、変える案として示し、合意を得てから変える。 改善点がないと判断した項目は変更しない。
リファクタリング中は振る舞いを変更しない。
REFACTOR 後は関連テストを再実行し、GREEN を維持していることを確認する。
6. 次のサイクル
次の振る舞いについて再び RED → GREEN → REFACTOR を繰り返す。
各サイクルで得た知見を次のテストへ反映する。
バグ修正
バグ修正では、最初にバグを再現する回帰テストを書く。
バグを自動テストで直接再現できない場合は、以下の順序で代替する。
- 再現可能な最も近い境界で回帰テストを書く
- 根本原因となるロジックを安定した境界から検証する
- 自動化できない部分とその理由を完了報告に記載する
自動テストを作れないことだけを理由に作業を停止しない。
症状だけを抑える workaround ではなく、根本原因を修正する。
既存テストがある場合
変更前に以下を確認する。
- 既に同じ振る舞いを検証していないか
- 既存テストを拡張することで同じ振る舞いを表現できないか
- repository 内で使用されているテストスタイルは何か
同じ振る舞いを重複してテストしない。
既存テストが実装詳細に密結合していても、タスクと無関係な大規模書き換えは行わない。 今回の変更対象と直接関係する範囲だけを改善する。
テスト粒度
テストは、対象の振る舞いを検証できる最小の安定した境界に置く。
テスト粒度の目安は次のとおり。
- 純粋なロジック: unit / module test
- モジュール間の契約: integration test
- ユーザーから見た主要フロー: E2E / acceptance test
同じ振る舞いを複数レイヤーで重複して検証しない。
例外処理
TDD の通常フローをそのまま適用できない状況でも、作業を止めない。 ただし、合意した内容を変える必要がある場合は、作業を止めてユーザーに確認する。
以下の順序で対応する。
- 何が通常フローを妨げているか特定する
- TDD の目的を維持できる最も近い代替手段を選ぶ
- 代替手段で検証する
- 通常フローから外れた点と理由を完了報告に記載する
ユーザー判断が必須となる破壊的操作、公開仕様の選択、互換性を壊す変更については、上位の指示または AGENTS.md に従う。
完了条件
実装完了前に以下をすべて確認する。
- 追加・変更した振る舞いがテストで検証されている
- すべての追加テストについて RED を確認している
- 各振る舞いで
RED → GREEN → REFACTORを完了している - 関連テストがすべて通っている
- repository で要求される型チェック、lint、build を実行している
- 実行できなかった検証は、代替検証を実施して理由を記録している
- タスクと無関係な変更が含まれていない