tdd-test-cases
TDD における高品質なテストケースをプリンシパルエンジニア視点で網羅的に洗い出す。 単なるユースケース充足を超え、冪等性・並列安全性・拡張性・セキュリティなど 実運用で問題になる観点を徹底的に掘り起こす。品質最優先で、時間をかけてでも ユーザーが気づいていない致命的な欠陥や論理の漏れを発見して問いかける。
Step 1: テスト対象を把握する
$ARGUMENTS を解析する:
- ファイルパスが含まれている場合 →
Readでファイル内容を取得し、 同ディレクトリの既存テストファイル(*.spec.*,*.test.*,*_test.*)をGlobで探す。 依存するモジュール・インターフェースもgrepで確認する。 - 仕様・機能説明テキストが含まれている場合 → そのままコンテキストとして扱う(ファイル読み込みは不要)。ただし Step 2 の必須確認事項は必ずチェックすること。
- 引数なし →
AskUserQuestionでテスト対象(機能概要・言語・フレームワーク)を確認する。
Step 1 完了後は必ず Step 2 に進み、必須確認事項をチェックすること。 テキストや仕様が提供されていても、言語・フレームワーク等の情報が不明な場合は Step 2 で確認する。 Step 2 をスキップして Step 3 に直接進んでよいのは、Step 2 の必須確認事項が全て判明している場合のみ。
Step 2: 致命的な欠陥・論理の漏れを確認する
仕様・コードを読んで以下の問題を発見した場合、または下記の情報が不明な場合は
必ず AskUserQuestion で確認してから先に進む。確認を省略すると
テストケースの品質が著しく低下するため、時間をかけてでも確認する。
必須確認事項(不明な場合は質問する)
| 確認事項 | なぜ重要か |
|---|---|
| 言語・フレームワーク・テストライブラリ | テスト粒度・命名規則・モック戦略が変わる |
| 副作用の有無(DB / ファイル / 外部API / メール / キュー等) | 冪等性・テスト分離の設計が変わる |
| 認証・認可の有無 | セキュリティテストケースの要否が変わる |
| 非同期処理・並列処理の有無 | 競合状態・タイムアウトテストが必要になる |
| 既存テストの有無とカバレッジ | 重複を避け、抜け漏れを補完できる |
見つけたら即座に質問すべき設計上の危険信号
以下を仕様・コードに発見した場合、テストケース生成を一時停止して
AskUserQuestion で問題を指摘し、設計意図を確認する:
- トランザクション境界が不明確: 複数の操作をアトミックに扱う必要があるか未定義
- 冪等性の保証がない: 同じ操作を 2 回呼んだ場合の仕様が定義されていない
- 並列実行時の競合: 共有状態への同時アクセスが考慮されていない
- エラー時のロールバック設計がない: 部分成功状態が発生しうる
- 認証なしの破壊的操作: 削除・更新操作に認可チェックがない
- 境界値の仕様が矛盾または曖昧: 最大値・最小値の定義が不整合
- 状態遷移の抜け漏れ: 特定の状態からの遷移が未定義
Step 3: 多角的な観点でテストケースを洗い出す
以下の観点を すべて検討 し、対象に該当するテストケースを列挙する。 「このシステムに当てはまらない」と判断した観点は、その理由を出力の末尾に簡潔にまとめる。
観点 A: テストピラミッドと配置
各テストケースに以下のレベルを割り当て、全体の比率を意識する。
| レベル | 粒度 | 目的 | 理想的な比率 |
|---|---|---|---|
| UT | 関数・クラス単位 | ロジックの正確性を最速で確認 | 約 70% |
| IT | 複数コンポーネント・DB 連携 | 結合時の動作を確認 | 約 20% |
| E2E | システム全体・UI / API 全体 | ユーザー視点の動作を確認 | 約 10% |
E2E に偏りすぎないこと。遅く・壊れやすいテストは CI のフィードバックループを壊す。
観点 B: 正常系(Happy Path)
- 最も一般的なユースケース
- 典型的な入力値での期待動作
- 戻り値・副作用(DB の状態変化・イベント発行など)の完全な検証
観点 C: 境界値・同値分割
- 最小値・最大値・その直前・直後(例: 長さ 0, 1, max-1, max, max+1)
- 空文字列・空配列・null / nil / undefined
- 型の境界(整数最大値
Int.MAX_VALUE・浮動小数点精度0.1 + 0.2) - 文字列長・配列長の制限
- 日付の境界(月末・年末・うるう年 2/29・タイムゾーン変換)
- ページネーション境界(0件・1件・最終ページ・ページ超過)
観点 D: 異常系・エラーハンドリング
- 不正な入力(型違い・範囲外・必須項目の欠如・不正フォーマット)
- 外部サービス障害時の振る舞い(タイムアウト・接続拒否・HTTP 5xx)
- 存在しないリソースへのアクセス(404 相当)
- 権限不足時の振る舞い(403 相当)
- エラーメッセージに機密情報(パスワード・トークン・内部パス)が含まれないこと
- 例外が適切にキャッチ・変換されること(スタックトレースの露出がないこと)
観点 E: 冪等性
同じ操作を N 回繰り返しても安全であることを確認する。 特に重要な操作:
- 作成操作の重複実行(重複レコードが生まれないか)
- 更新操作の重複適用(2 回適用しても同じ結果か)
- 削除操作の重複実行(2 回目が正常終了するか・エラーになるか仕様を明確化)
- HTTP PUT / DELETE の冪等性
- メッセージキューの重複配信への対応(at-least-once 配信時)
- 外部 API 呼び出し失敗後のリトライ時の冪等性
観点 F: 並列実行安全性・競合状態
- 同一リソースへの同時書き込み(在庫・残高・カウンタ)
- 楽観的ロック・悲観的ロックの動作確認
- トランザクション分離レベルの検証(ファントムリード・ダーティリード等)
- 非同期処理の完了順序依存(A より先に B が完了した場合)
- キャッシュの競合(Cache-Aside パターンの同時書き込み)
- テスト自体が並列実行されても互いに干渉しないこと(テスト独立性)
- 共有状態のリセット(グローバル変数・シングルトン・環境変数)
観点 G: セキュリティ
- インジェクション: SQL インジェクション・NoSQL インジェクション・コマンドインジェクション
- XSS: 出力のエスケープ、ユーザー入力の HTML 挿入
- パストラバーサル: ファイルパスに
../を含む入力 - 認証バイパス: 未認証アクセス・期限切れトークンの使用・セッション固定
- 認可バイパス: 他ユーザーのリソースへのアクセス(IDOR)
- 大量リクエスト: レートリミットの動作確認
- 機密情報の漏洩: ログ・エラーレスポンスへのパスワード・トークン混入
観点 H: パフォーマンス・スケーラビリティ
- 大量データ(N 件)処理時の性能劣化(O(n²) のアルゴリズムの検出)
- N+1 クエリ問題(ループ内でのクエリ発行がないか)
- ページネーションの正確性(件数・順序・境界)
- キャッシュの有効期限・無効化(stale なデータが返らないか)
- タイムアウト設定の適切さ(無限待機が発生しないか)
- メモリリーク(長時間実行時の挙動)
観点 I: 将来の拡張性を考慮したテスト設計
注意: この観点は他の観点(B〜H)と異なり、個別のテストケースを列挙するためのものではない。 観点 B〜H で設計した全テストケースに横断適用する品質チェックリストである。 観点 I 専用のテストケースを作るのではなく、各テストケースを設計する際に以下を確認すること。
以下は「悪いテスト設計」の検出指針として使う:
- 実装の内部詳細(プライベートメソッド・内部状態)に依存したテストは避ける
- 「戻り値の型が辞書型でキーが
data」のような実装依存の検証は壊れやすい - モックの過剰使用(結合テストで全依存をモックにすると実際の統合問題を見逃す)
- テストデータに将来フィールドが追加されたときに壊れる構造(ワイルドカードで比較)
- 1 テストで複数の振る舞いを検証しているもの(1 テスト = 1 観点の原則)
観点 J: テスト独立性・再現性
- テストの実行順序に依存していないか(前テストの副作用を引き継がない)
- 外部環境への依存を制御しているか(時刻:
freeze_time・乱数: シード固定・環境変数) - テスト後のクリーンアップが確実に行われるか(teardown / afterEach)
- フィクスチャ・シードデータの競合がないか(並列テスト実行時)
- ネットワーク・ファイルシステムへのアクセスが UT に紛れ込んでいないか
Step 4: 出力する
以下のフォーマットで出力する。テストケースが 30 件を超える場合は観点ごとにセクションを分割する。
テストケース一覧: <テスト対象名>
サマリー
| 項目 | 内容 |
|---|---|
| テスト対象 | ... |
| 技術スタック | ... |
| 総テストケース数 | N 件(UT: X / IT: Y / E2E: Z) |
| カバーした観点 | A, B, C, ... |
| スキップした観点と理由 | G: 認証機能なし のため不要、 など |
テストケース
| # | テスト名 | レベル | 観点 | テスト内容(Given / When / Then) | 優先度 |
|---|---|---|---|---|---|
| 1 | ... | UT | 正常系 | Given: ... / When: ... / Then: ... | P0 |
| 2 | ... | UT | 境界値 | ... | P0 |
優先度定義
| 優先度 | 基準 |
|---|---|
| P0 | 致命的 — これが通らなければリリース不可 |
| P1 | 重要 — バグが本番で影響を与えうる |
| P2 | 品質向上 — エッジケース・将来の拡張に備える |
設計上の懸念点・推奨事項
(Step 2 で発見した危険信号や、TDD で実装する際のヒントをここに記載)
TDD 実装順序の提案
P0 → P1 → P2 の順に実装することを推奨する。 ただし、以下の観点で順序を調整する:
- 最も単純な正常系から始める — Red-Green-Refactor の最初のサイクルを確立する
- 境界値・異常系は正常系が安定してから追加する — テストが多すぎると初期実装が複雑になる
- 並列・冪等性テストは最後に追加する — 実装が固まってから検証する