# E2Eテスト知識

## E2Eテストのスコープ

E2Eテストはユーザーの操作フロー全体を検証する。ユニットテストやインテグレーションテストとはスコープが異なる。

| テスト種別 | スコープ | 検証対象 |
|-----------|---------|---------|
| ユニット | 関数/クラス | ロジックの正しさ |
| インテグレーション | モジュール間結合 | データフローの正しさ |
| E2E | ユーザー操作フロー全体 | ユーザーから見た振る舞い |

| 基準 | 判定 |
|------|------|
| ユニットテストでカバーできるロジック検証をE2Eで書く | 警告。ユニットテストに移動を検討 |
| ユーザーが実行する操作フローの検証 | E2Eテストが適切 |
| 複数コマンド/ページにまたがるシナリオ | E2Eテストが適切 |
| エラーメッセージの表示確認 | E2Eテストが適切 |

## UXルートの洗い出し

E2Eテストの網羅性はユーザー操作ルートの洗い出しに依存する。ドキュメントではなくコードから起点を特定する。

### 起点の特定方法

| アプリケーション種別 | 起点の探し方 |
|-------------------|------------|
| CLI | コマンド定義、サブコマンド登録、オプション/フラグ定義をコードから抽出 |
| Web | ルーティング定義、ページコンポーネント一覧をコードから抽出 |
| API | エンドポイント定義、ルーター登録をコードから抽出 |

### 分岐パターン

各起点から分岐するルートを網羅的に列挙する。

| 分岐パターン | 例 |
|------------|-----|
| オプション/フラグの組み合わせ | `--verbose` あり/なし、`--format json` vs `--format table` |
| 状態による分岐 | 初回実行 vs 既存データあり、設定ファイルあり vs なし |
| 権限/ロール | 管理者 vs 一般ユーザー、認証済み vs 未認証 |
| 外部依存の状態 | 接続成功 vs タイムアウト、正常レスポンス vs エラーレスポンス |
| エラーリカバリー | 途中で失敗した場合の再試行、ロールバック |
| 入力バリエーション | 有効な入力、無効な入力、空入力、境界値 |


## モック境界の設計

E2Eテストでは「どこまで実物で動かし、どこからモックするか」の判断が重要。

### モック設計の原則

- テスト対象のアプリケーションコードはそのまま動かす
- 外部サービスとの境界でモックを挿入する
- モックは既存のフィクスチャ・ヘルパーのパターンに合わせる
- 新しいモック機構を導入する前に既存のものを確認する

## フレイキーテスト対策

E2Eテストは非決定的な要因で失敗しやすい。

| 原因 | 対策 |
|------|------|
| タイミング依存 | 明示的な待機条件を使う（固定 sleep ではなく状態ベースの待機） |
| ポート競合 | テストごとにランダムポートを割り当てる |
| ファイルシステムの残留 | テストごとに一時ディレクトリを作成し、終了時にクリーンアップ |
| プロセスリーク | タイムアウトと強制終了を設定する |
| 環境依存 | テスト実行に必要な前提条件を明示的にセットアップする |
| 実行順序依存 | 各テストが独立して実行できるよう、状態を初期化する |

```typescript
// NG - 固定 sleep でタイミングを合わせる
await sleep(3000)
expect(result).toBeDefined()

// OK - 条件ベースで待機する
await waitFor(() => expect(result).toBeDefined(), { timeout: 5000 })
```

## テストケース管理

E2Eテストの網羅性を保証するため、テストケースをリストで管理する。

| 原則 | 内容 |
|------|------|
| 番号付きリスト | 各テストケースに一意の番号を付与し、実装状況を追跡する |
| 起点ごとに分類 | コマンド/ページ/エンドポイント単位でグループ化する |
| 優先度付け | ユーザー影響度 × 未テストのリスクで優先度を決定する |
| 既存テストとの突き合わせ | 新規追加前に既存テストのカバー範囲を確認する |
