| name | tdd |
| description | レッド・グリーン・リファクタのループによるテスト駆動開発。ユーザーがTDDで機能開発やバグ修正をしたい場合、「レッド・グリーン・リファクタ」に言及した場合、統合テストを求めた場合、またはテストファースト開発を依頼した場合に使用します。 |
| ref | https://github.com/mattpocock/skills/tree/main/tdd.SKILL.md |
テスト駆動開発
哲学
基本原則: テストは実装の詳細ではなく、公開インターフェースを通じて振る舞いを検証すべきです。コードは完全に変わる可能性がありますが、テストは変わるべきではありません。
良いテストは統合テストスタイルです。公開APIを通じて実際のコードパスを実行します。テストはシステムが 何を するかを記述し、どのように するかは記述しません。良いテストは仕様書のように読めます。「ユーザーは有効なカートでチェックアウトできる」と書けば、どんな機能が存在するかが正確にわかります。これらのテストは内部構造を気にしないため、リファクタリングに耐えられます。
悪いテストは実装に結合しています。内部の協調オブジェクトをモックしたり、プライベートメソッドをテストしたり、外部手段(インターフェースを使わずにデータベースを直接クエリするなど)で検証したりします。警告サイン:リファクタリングするとテストが壊れるのに、振る舞いは変わっていない場合です。内部関数の名前を変えてテストが失敗するなら、それらのテストは振る舞いではなく実装をテストしていたのです。
詳しくはtests.mdの例とmocking.mdのモッキングガイドラインを参照してください。
アンチパターン: 水平スライス
すべてのテストを先に書いてから、すべての実装を書くことはしないでください。 これは「水平スライス」です。REDを「すべてのテストを書く」、GREENを「すべてのコードを書く」として扱っています。
これは質の悪いテストを生み出します:
- 一括で書かれたテストは 実際の 振る舞いではなく、想像上の 振る舞いをテストします
- データ構造や関数シグネチャなど、物の 形 をテストすることになり、ユーザー向けの振る舞いをテストしなくなります
- テストは実際の変更に鈍感になります。振る舞いが壊れても通過し、振る舞いが正常でも失敗します
- ヘッドライトの先を走りすぎて、実装を理解する前にテスト構造にコミットしてしまいます
正しいアプローチ: トレーサーバレットによる垂直スライス。1つのテスト → 1つの実装 → 繰り返し。各テストは前のサイクルから学んだことに応じて書きます。コードを書いたばかりなので、どの振る舞いが重要で、どう検証すべきかを正確に把握しています。
間違い(水平):
RED: test1, test2, test3, test4, test5
GREEN: impl1, impl2, impl3, impl4, impl5
正しい(垂直):
RED→GREEN: test1→impl1
RED→GREEN: test2→impl2
RED→GREEN: test3→impl3
...
ワークフロー
1. 計画
コードを書く前に:
質問する:「公開インターフェースはどのような形にすべきですか?テストすべき最も重要な振る舞いはどれですか?」
すべてをテストすることはできません。 どの振る舞いが最も重要かをユーザーと正確に確認してください。あらゆるエッジケースではなく、クリティカルパスと複雑なロジックにテストの労力を集中させましょう。
2. トレーサーバレット
システムについて1つのことを確認する1つのテストを書きます:
RED: 最初の振る舞いのテストを書く → テストが失敗する
GREEN: テストを通す最小限のコードを書く → テストが通る
これがトレーサーバレットです。パスがエンドツーエンドで機能することを証明します。
3. インクリメンタルループ
残りの各振る舞いについて:
RED: 次のテストを書く → 失敗する
GREEN: テストを通す最小限のコード → 通る
ルール:
- 一度に1つのテスト
- 現在のテストを通すのに十分なコードだけ書く
- 将来のテストを先回りしない
- テストは観測可能な振る舞いに焦点を当てる
4. リファクタリング
すべてのテストが通った後、リファクタリング候補を探します:
REDの状態でリファクタリングしないこと。 まずGREENにしてください。
サイクルごとのチェックリスト
[ ] テストは実装ではなく振る舞いを記述している
[ ] テストは公開インターフェースのみを使用している
[ ] テストは内部リファクタリングに耐えられる
[ ] コードはこのテストに対して最小限である
[ ] 投機的な機能が追加されていない