テスト駆動開発
哲学
核となる原則: テストは内部実装ではなく、パブリックインターフェースを通じて振る舞いを検証すべきです。コードは完全に変更されても、テストは壊れてはいけません。
良いテストは統合スタイルです。パブリック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. 計画
コードベースを探索する際は、プロジェクトのドメイン用語集を使用して、テスト名とインターフェースの語彙がプロジェクトの言語と一致するようにし、触れている領域のADRを尊重してください。
コードを書く前に:
- ユーザーに必要なインターフェース変更を確認する
- ユーザーにテストすべき振る舞いを確認する(優先順位付け)
- 深いモジュール(小さなインターフェース、深い実装)の機会を特定する
- テスト容易性のためにインターフェースを設計する
- テストすべき振る舞いをリストする(実装手順ではない)
- 計画についてユーザーの承認を得る
質問:「パブリックインターフェースはどのようにあるべきですか?どの振る舞いが最も重要ですか?」
すべてをテストすることはできません。 どの振る舞いが最も重要かをユーザーと正確に確認してください。テストの労力をすべての可能なエッジケースではなく、クリティカルパスと複雑なロジックに集中させてください。
2. トレーサーバレット
システムについて 1つ のことを確認する 1つ のテストを書きます:
RED: 最初の振る舞いのテストを書く → テスト失敗
GREEN: 通過するための最小限のコードを書く → テスト成功
これがトレーサーバレットです — パスがエンドツーエンドで機能することを証明します。
3. インクリメンタルループ
残りの各振る舞いに対して:
RED: 次のテストを書く → 失敗
GREEN: 通過するための最小限のコード → 成功
ルール:
- 一度に1つのテスト
- 現在のテストを通過するのに十分なコードだけ
- 将来のテストを予測しない
- テストは観測可能な振る舞いに集中し続ける
4. リファクタリング
すべてのテストが通った後、リファクタリング候補を探します:
- 重複を抽出する
- モジュールを深くする(複雑さをシンプルなインターフェースの背後に移動)
- 自然な場所にSOLID原則を適用する
- 新しいコードが既存のコードについて何を明らかにするかを検討する
- リファクタリングの各ステップ後にテストを実行する
REDの状態ではリファクタリングしないでください。 まずGREENにしてください。
サイクルごとのチェックリスト
[ ] テストは実装ではなく振る舞いを記述している
[ ] テストはパブリックインターフェースのみを使用している
[ ] テストは内部リファクタリング後も生き残る
[ ] コードはこのテストに対して最小限である
[ ] 推測による機能追加がない