Behavioral Testing
目的
AIがテストコードを書く時に、数や網羅率ではなく、退行を検出し、リファクタリングを妨げず、保守しやすいテストを作る。テストは資産ではなく保守コストを持つ負債でもあるため、価値がコストを上回る場合だけ追加する。
このスキルでは、単体テストを「1単位の振る舞いを、速く、他のテストから隔離して検証するテスト」と捉える。焦点はクラスやメソッドではなく、観察可能な振る舞いである。
最初に止まる条件
次の条件に当てはまる場合は、テスト実装に入る前に質問するか、characterization test に限定するか、作業を止める。
- 期待値の根拠が現在の実装しかない。
- 仕様、issue、PRD、公開API契約、バグ再現条件、既存の明示的期待値のどれも見つからない。
- 境界条件、丸め、時刻の包含/排他などの仕様が未確定なのに、AIが期待値を補う必要がある。
- private method や内部呼び出ししか観察点がない。
- 既存のテスト規約、fixture/factory、実行コマンド、外部依存の扱いを確認していない。
- 外部サービスへ実通信しないと成立しない。
- テストを通すためだけにプロダクションコードを汚染しそうである。
判断フロー
- 仕様オラクルを特定する。 仕様、issue、PRD、公開API契約、ユーザー報告、バグ再現手順、既存の明示的な期待値を探す。実装の分岐や定数を丸写しして期待値を作らない。
- 観察可能な振る舞いを1つ選ぶ。 クライアントが達成したい結果、公開APIの戻り値、公開状態、外部から観察できる副作用に絞る。
- コードを分類する。 ドメイン/アルゴリズムは単体テスト、コントローラは統合テスト、取るに足らないコードは原則テストしない。複雑さと協力者が両方多いコードは、先に責務分離を提案する。
- 依存を分類する。 管理下依存は実物で状態を確認する。管理下にないプロセス外依存だけを、所有する境界アダプタのモックまたはスパイに置き換える。システム内コミュニケーションは検証しない。
- テスト手法を選ぶ。 出力値ベースを最優先する。必要に応じて状態ベース、ドメインイベント、統合テストでの境界スパイを選ぶ。コミュニケーションベースは外部から観察可能な境界通信に限定する。
- 既存規約に合わせて実装する。 既存のテストランナー、命名、fixture、factory、assertion library、DB初期化、cleanup、CIコマンドに合わせる。
- 最小の検証コマンドを実行する。 追加・変更したテストを最小範囲で実行し、必要なら関連テストまで広げる。
実装前チェック
テストを書く前に、短くてもよいので次を自分の中で埋める。
仕様根拠:
観察する振る舞い:
テスト対象分類:
テストレベル:
依存の扱い:
使う既存fixture/factory:
時刻/乱数/並行性/外部I/Oの扱い:
実行コマンド:
この表を埋められない場合、テストを書くよりも先に不足情報を調べる。調べても埋まらない場合は、ユーザーに確認する。
モックのルール
- 単体テストで安易にモックを作らない。設計が悪くてテストしづらい箇所をモックで隠さない。
- スタブは入力を供給するために使う。スタブとのやり取りは検証しない。
- モック/スパイは外向きのコミュニケーションを検証するために使う。
- モック対象は自分たちが所有する境界アダプタにする。サードパーティSDKや具象クラスを直接モックしない。
- モックを使う場合は、期待する呼び出しと、起きてはいけない呼び出しの両方を検討する。
テストの形
- AAA(Arrange, Act, Assert)を保つ。同じフェーズが複数回出るなら、複数の振る舞いを1つのテストに詰めている可能性を疑う。
- Act が複数行になる場合、公開APIの設計や抽象化不足を疑う。
- テスト名は実装名ではなく、ドメイン上の事実や振る舞いを表す。
- 過度な共有fixtureより、テスト内で意図が読めるfactory/helperを優先する。
- カバレッジ稼ぎ、同義反復assert、実装ロジックのコピー、広すぎるsnapshotを避ける。
- 境界条件が仕様にない場合、テストで勝手に固定しない。確認するか、仕様化済みの範囲だけをテストし、未確定境界は明示的に保留する。
参照ファイル
- テスト戦略、単体/統合境界、コード分類、依存分類、モック判断で迷う場合は
references/testing-principles.mdを読む。 - テスト実装に入る場合は
references/ai-test-writing-protocol.mdを読む。 - 既存テストのレビュー、壊れやすいテスト、過剰モック、snapshot乱用を扱う場合は
references/test-smells-and-refactor-recipes.mdを読む。 - DBを含む統合テストを扱う場合は
references/db-integration-testing.mdを読む。
出力方針
- テストを追加した場合は、何を仕様根拠にしたか、どの振る舞いを守るか、どのコマンドで検証したかを報告する。
- テストを書かない判断をした場合は、なぜ価値がコストを下回るか、または何の仕様根拠が不足しているかを明確に伝える。
- テスト容易性のためのリファクタリングが必要な場合は、テストだけで隠さず、最小の設計改善として提案する。