--- name: integration-e2e-testing description: 統合テストとE2Eテストを設計。モック境界と振る舞い検証ルールを適用。E2Eテスト、統合テスト作成時に使用。 --- # 統合テスト・E2Eテスト設計・実装ルール ## References - **[references/e2e-design.md](references/e2e-design.md)** — E2Eテスト設計原則(候補ソース、選定基準、UI Specからのマッピング) - **[references/e2e-environment-prerequisites.md](references/e2e-environment-prerequisites.md)** — E2E環境前提条件(seed data、auth fixture、環境チェックリスト) ## テスト種別と上限 | 種別 | 目的 | ファイル形式 | 上限 | |------|------|-------------|------| | 統合テスト | コンポーネント間連携検証 | `*.int.test.ts` | 機能あたり3件 | | E2Eテスト | クリティカルユーザージャーニー検証 | `*.e2e.test.ts` | 機能あたり1-2件 | **クリティカルユーザージャーニー**: 収益影響・法的要件・大多数のユーザーが日常的に利用する機能 ## 振る舞い優先の原則 ### 観測可能性チェック(全てYESで対象) | チェック | 質問 | NOの場合 | |---------|------|----------| | 観測可能 | ユーザーが結果を観測できるか? | 除外 | | システム文脈 | 複数コンポーネントの統合が必要か? | 除外 | | 自動化可能 | CI環境で安定実行できるか? | 除外 | ### Include/Exclude基準 **Include**: ビジネスロジック正確性、データ整合性、ユーザー可視機能、エラーハンドリング **Exclude**: 外部実接続、パフォーマンス指標、実装詳細、UIレイアウト ## スケルトン仕様 ### 必須コメント形式 各テストに以下のアノテーションを含めること。 ```typescript // AC: "[受入条件原文]" // ROI: [0-100] | ビジネス価値: [0-10] | 頻度: [0-10] // 振る舞い: [トリガー] → [処理] → [観測可能な結果] // @category: core-functionality | integration | edge-case | ux | e2e // @dependency: none | [コンポーネント名] | full-system // @complexity: low | medium | high // @real-dependency: [コンポーネント名](任意、テスト境界で非モックセットアップが指定された場合) it.todo('[AC番号]: [テスト名]') ``` ### Property注釈 ```typescript // Property: `[検証式]` // fast-check: fc.property(fc.[arbitrary], (input) => [不変条件]) it.todo('[AC番号]-property: [不変条件記述]') ``` ## マルチステップユーザージャーニーの定義 以下の3条件を**すべて**満たす場合、その機能にはマルチステップユーザージャーニーが含まれる: 1. **2つ以上の異なるインタラクション境界**をユーザーゴール達成のために順番に通過する。何が境界に該当するかはシステムタイプによる: - Web: 異なるルート/ページ - モバイルネイティブ: 異なる画面/ビュー - CLI: 異なるコマンド呼び出しまたは対話型プロンプト - API: トランザクションを形成する異なるAPI呼び出し(例: 作成 → 確認 → 確定) 2. **ステップ間で状態が伝搬する** — あるステップで生成されたデータや実行されたアクションが、次のステップの受け入れや表示に影響する 3. **ジャーニーに完了ポイントがある** — ユーザーまたは呼び出し元が到達する最終状態(例: 確認ページ、保存されたレコード、API成功レスポンス、完了したワークフロー) ### ユーザー向け vs サービス内部の分類 マルチステップジャーニーはE2E予算判断のためにさらに分類される: | 分類 | 条件 | E2E予約スロット | 例 | |------|------|----------------|-----| | **ユーザー向け** | 人間のユーザーが直接ステップをトリガーし結果を観察する(UI、CLI、または直接的なAPIインタラクション経由) | 対象 | Web購入フロー、CLIセットアップウィザード、モバイルオンボーディング | | **サービス内部** | バックエンドサービスがユーザーの直接操作なしにステップをトリガーする | 予約スロット対象外 | 非同期ジョブパイプライン、サービス間saga、スケジュールバッチ処理 | **この分類のスコープ**: - **E2E予約スロット**: ユーザー向けジャーニーのみが対象。サービス内部ジャーニーは予約スロットから除外される。 - **通常のROI > 50パス**: ユーザー向け・サービス内部の両方が追加E2Eスロット(最大1件)をROI実績で競う。この分類はこのパスに影響しない。 - **E2Eギャップチェック**: ユーザー向けジャーニーのみがgap警告をトリガーする。サービス内部ジャーニーはトリガーしない。 ### ROI計算 ``` ROI Score = Business Value × User Frequency + Legal Requirement × 10 + Defect Detection(範囲: 0–120) ``` ROI Scoreは**同一テスト種別内での優先順位付け**に使用する(統合テスト同士、E2Eテスト同士)。テスト種別間の比較には使用しない。統合とE2Eの予算は独立して選択されるため、種別間比較は不要。 ROI Scoreが高い = 同一テスト種別内での優先度が高い。正規化やキャッピングは適用しない — 生のスコアをそのままランキングに使用する。重複排除は候補自体を除外する別のステップであり、スコアは変更しない。 #### E2EのROI閾値 E2Eテストは所有コストが高い(作成・実行・保守それぞれが統合テストの3〜10倍)。予約スロット(ユーザー向けマルチステップジャーニー)を超える追加E2Eスロットには **ROI Score > 50** が必要。 #### ROI計算例 | シナリオ | BV | Freq | Legal | Defect | ROI Score | テスト種別 | 選択結果 | |----------|----|------|-------|--------|-----------|-----------|---------| | コア決済フロー | 10 | 9 | true | 9 | 109 | E2E | 選択(予約スロット: ユーザー向けマルチステップジャーニー) | | 決済エラー処理 | 8 | 3 | false | 7 | 31 | E2E | 閾値未満(31 < 50)、不選択 | | プロフィール保存フロー | 7 | 6 | false | 6 | 48 | E2E | 閾値未満(48 < 50)、不選択 | | DB永続化チェック | 8 | 8 | false | 8 | 72 | 統合 | 選択(3件中ランク1位) | | エラーメッセージ表示 | 5 | 3 | false | 4 | 19 | 統合 | 選択(3件中ランク2位) | | 任意フィルタ切替 | 3 | 4 | false | 2 | 14 | 統合 | 不選択(ランク4位、上限到達) | ## 実装ルール ### Property-Based Test実装 Property注釈がある場合、fast-checkライブラリ必須: ```typescript import fc from 'fast-check' it('AC2-property: モデル名は常にgemini-3-pro-image-preview', () => { fc.assert( fc.property(fc.string(), (prompt) => { const result = client.generate(prompt) return result.model === 'gemini-3-pro-image-preview' }) ) }) ``` **必須事項**: - `fc.assert(fc.property(...))` 形式で記述 - スケルトンの`// fast-check:`コメントをそのまま実装に反映 - 失敗ケース発見時は具体的なユニットテストとして追加(リグレッション防止) ### 振る舞い検証の実装 **振る舞い記述の検証レベル**: | ステップ種別 | 検証対象 | 例 | |-------------|---------|-----| | トリガー | Arrangeで再現 | API障害 → mockResolvedValue({ ok: false }) | | 処理 | 中間状態または呼び出し | 関数呼び出し、状態変更 | | 観測可能な結果 | 最終出力の値 | 戻り値、エラーメッセージ、ログ出力 | **判定基準**: 「観測可能な結果」がテスト対象の**戻り値またはモックの呼び出し引数**として検証されていれば合格 ### 検証項目の決定ルール | スケルトンの状態 | 検証項目の決定方法 | |-----------------|-------------------| | `// 検証項目:` が列挙されている | 列挙された全項目をexpectで実装 | | `// 検証項目:` がない | 「振る舞い」記述の「観測可能な結果」から導出 | | 両方ある | 検証項目を優先、振る舞いは補足として使用 | ### 統合テストのモック境界 | 判断基準 | モック | 実物 | |---------|--------|------| | テスト対象の一部か? | No → モック可 | Yes → 実物必須 | | 呼び出しがテストの検証対象か? | No → モック可 | Yes → 実物または検証可能なモック | | 外部ネットワーク通信か? | Yes → モック必須 | No → 実物推奨 | **判定フロー**: 1. 外部API(HTTP通信)→ モック必須 2. テスト対象のコンポーネント間連携 → 実物必須 3. ログ出力の検証が必要 → 検証可能なモック(vi.fn())を使用 4. ログ出力の検証が不要 → 実物または無視 ### E2Eテストの実行条件 - 全コンポーネント実装完了後に実行 - モック禁止(`@dependency: full-system`) ## レビュー基準 ### スケルトンと実装の整合性 | チェック | 不合格条件 | |---------|-----------| | Property検証 | Property注釈があるのにfast-check未使用 | | 振る舞い検証 | 「観測可能な結果」に対応するexpectがない | | 検証項目網羅 | 列挙された検証項目がexpectに含まれていない | | モック境界 | 統合テストで内部コンポーネントをモック化 | ### 実装品質 | チェック | 不合格条件 | |---------|-----------| | AAA構造 | Arrange/Act/Assertの区切りが不明確 | | 独立性 | テスト間で状態共有、実行順序依存 | | 再現性 | 日時・乱数に依存し結果が変動 | | 可読性 | テスト名と検証内容が一致しない |