# テストポリシー

全ての振る舞いの変更には対応するテストが必要であり、全てのバグ修正にはリグレッションテストが必要。

## 原則

| 原則 | 基準 |
|------|------|
| Given-When-Then | テストは3段階で構造化する |
| 1テスト1概念 | 複数の関心事を1テストに混ぜない |
| 振る舞いを検証 | 実装の詳細ではなく振る舞いをテストする |
| 独立性 | 他のテストや実行順序に依存しない |
| 再現性 | 時間やランダム性に依存せず、毎回同じ結果 |
| 非実行資産を固定しない | 実行時の振る舞いを定義しない本文や章構成を CI の失敗条件にしない |

## カバレッジ基準

| 対象 | 基準 |
|------|------|
| 新しい振る舞い | テスト必須。テストがなければ REJECT |
| バグ修正 | リグレッションテスト必須。テストがなければ REJECT |
| 振る舞いの変更 | テストの更新必須。更新がなければ REJECT |
| ビルド（型チェック） | ビルド成功必須。失敗は REJECT |
| エッジケース・境界値 | テスト推奨（Warning） |

## テスト優先度

| 優先度 | 対象 |
|--------|------|
| 高 | ビジネスロジック、状態遷移 |
| 中 | エッジケース、エラーハンドリング |
| 低 | 単純なCRUD |

**注意:** デザイン参照が指定されている場合、UIの見た目の検証は中優先度に格上げする。デザイン忠実再現ポリシーを参照。

## 非実行資産のテスト

説明文、ガイド、README、Markdown ドキュメントなど、実行時の振る舞いを定義しない非実行資産の本文・見出し・構成を固定するテストは原則禁止。
これらは表現改善や整理で頻繁に変わるため、本文差分を CI 失敗条件にすると保守コストが高くなる。

| 基準 | 判定 |
|------|------|
| 非実行資産の本文・見出し・章構成の一致検証 | REJECT |
| 表記ゆれ検出だけを目的に非実行資産全体を走査するテスト | REJECT |
| 削除・統合され得る説明ファイルの存在前提テスト | REJECT |
| 実行契約を持たない docs-only 変更にテストを追加する | REJECT |
| CLI例・設定例・生成物など、実行可能または機械処理される契約の検証 | OK |
| schema、設定、コード、生成器など実行時挙動に関わる契約検証 | OK |
| 実行契約を持たない docs-only 変更でテストを追加しない判断 | OK |

非実行資産の変更確認は、必要に応じてレビュー、Markdown lint、リンクチェック、サンプルコマンドの実行で行う。

## テスト構造: Given-When-Then

```typescript
test('ユーザーが存在しない場合、NotFoundエラーを返す', async () => {
  // Given: 存在しないユーザーID
  const nonExistentId = 'non-existent-id'

  // When: ユーザー取得を試みる
  const result = await getUser(nonExistentId)

  // Then: NotFoundエラーが返る
  expect(result.error).toBe('NOT_FOUND')
})
```

## テスト品質

| 観点 | 良い | 悪い |
|------|------|------|
| 独立性 | 他のテストに依存しない | 実行順序に依存 |
| 型安全 | コードはビルド（型チェック）が通ること |
| 再現性 | 毎回同じ結果 | 時間やランダム性に依存 |
| 明確性 | 失敗時に原因が分かる | 失敗しても原因不明 |
| 焦点 | 1テスト1概念 | 複数の関心事が混在 |

## テストデータとフィクスチャ

テストデータは、テストごとに必要最小限の事実を明示して生成する。共有フィクスチャの破壊的変更や、実契約と異なるモックはテストの信頼性を下げる。

| 基準 | 判定 |
|------|------|
| テスト間で共有する fixture を変更して使い回している | REJECT |
| モック、fixture、factory が実際の型・API契約と異なる形を返している | REJECT |
| テストごとに巨大な全項目 fixture を直書きしている | 警告。factory 化を検討 |
| factory でデフォルトを用意し、テストに関係するフィールドだけ上書きしている | OK |
| 契約変更時に fixture、mock、snapshot を同じ変更で更新している | OK |

### 命名

テスト名は期待される振る舞いを記述する。`should {期待する振る舞い} when {条件}` パターンを使う。

### 構造

- Arrange-Act-Assert パターン（Given-When-Then と同義）
- マジックナンバー・マジックストリングを避ける

## 再取得ループのリグレッション

画面の初期取得がある場合、無関係な再レンダ、loading 切替、Context callback の参照変化で API が再実行されないことをテストで担保する。

| 基準 | 判定 |
|------|------|
| 初期取得バグ修正に対し、重複 API 呼び出しの回帰テストがない | REJECT |
| 1回呼ばれたことだけを確認し、再レンダ後の安定性を見ていない | 警告 |
| rerender や state 更新後も呼び出し回数が増えないことを検証している | OK |

## 到達経路のリグレッション

利用者向け機能や画面を追加・変更した場合、利用者がその機能へ到達できることをテストまたは同等の検証で担保する。

| 基準 | 判定 |
|------|------|
| 新規の画面・機能を追加したのに、到達経路や起動条件の検証がない | REJECT |
| 画面ファイル単体の描画だけを見て、入口からの到達確認をしていない | 警告 |
| route、メニュー、ボタン、リンク、外部呼び出しなど実際の入口から対象機能へ到達できることを確認している | OK |

## UIライブラリ統合のリグレッション

DataGrid、日付ピッカー、仮想リスト、チャートなど、外部 UI ライブラリの主要コンポーネントを導入・変更した場合は、実コンポーネントをマウントするテストでクラッシュしないことを担保する。

| 基準 | 判定 |
|------|------|
| 外部 UI ライブラリの主要コンポーネントを追加・変更したのに、実マウントの回帰テストがない | REJECT |
| ライブラリの props 整合性を、浅いモックや存在確認だけで済ませている | 警告 |
| route から対象画面を描画し、主要 UI が例外なくマウントされることを確認している | OK |
| 主要 UI コンポーネント単体でも、代表的な props で実 render している | OK |

## テスト戦略

- ロジックにはユニットテスト、境界にはインテグレーションテストを優先
- ユニットテストでカバーできるものにE2Eテストを使いすぎない
- 新しいロジックにE2Eテストしかない場合、ユニットテストの追加を提案する

### インテグレーションテストが必須な場面

ユニットテストだけでは検証できないデータフローの結合を検証する。

| 条件 | 判定 |
|------|------|
| 3つ以上のモジュールを横断するデータフロー | インテグレーションテスト必須 |
| 新しいステータス／状態が既存のワークフローに合流する | 遷移フロー全体のインテグレーションテスト必須 |
| 新しいオプションが呼び出しチェーンを通じて末端まで伝搬する | チェーン全体の結合テスト必須 |
| 各モジュールのユニットテストが全てパスしている | ユニットテストのみで十分（上記に該当しない場合） |

## ユニットテスト基準

| 基準 | 判定 |
|------|------|
| テスト対象の内部実装をモックする（振る舞いではなく実装を検証） | REJECT |
| テスト間でフィクスチャを共有して変更する | REJECT。テスト独立性の喪失 |
| モックの戻り値が実際の型と乖離している | 警告。型安全なモックを使う |
| 正常系のみテストして境界値がない | 警告 |

## E2Eテスト基準

E2E は、利用者が実際に入る起点から設計する。ドキュメント上の想定だけでなく、route、command、endpoint、navigation、button、external callback などコード上の入口を基準にする。

| 基準 | 判定 |
|------|------|
| 起点を確認せず、想像上の操作フローだけで E2E を書いている | REJECT |
| 外部API呼び出しをモックせず本番APIを叩く | REJECT。テストの再現性が失われる |
| テスト対象のコア処理をモックする | REJECT。E2Eの意味がなくなる |
| 固定 sleep でタイミングを合わせる | REJECT。状態ベースの待機を使う |
| テスト間で共有状態を持つ | 警告。テストの独立性が損なわれる |
| 正常フローだけテストして異常フローがない | 警告 |
| ユニットテストでカバーできるロジック検証をE2Eで書く | 警告 |

## テスト環境の分離

テストインフラの設定はテストシナリオのパラメータに連動させる。ハードコードされた前提は別シナリオで壊れる。

| 原則 | 基準 |
|------|------|
| パラメータ連動 | テストの入力パラメータに応じてフィクスチャ・設定を生成する |
| 暗黙の前提排除 | 特定の環境（ユーザーの個人設定等）に依存しない |
| 整合性 | テスト設定内の関連する値は互いに矛盾しない |
| プロセス終了保証 | テストランナーにタイムアウトと強制終了を設定し、プロセスリークを防ぐ |

```typescript
// ❌ ハードコードされた前提 — 別のバックエンドでテストすると不整合になる
writeConfig({ backend: 'postgres', connectionPool: 10 })

// ✅ パラメータに連動
const backend = process.env.TEST_BACKEND ?? 'postgres'
writeConfig({ backend, connectionPool: backend === 'sqlite' ? 1 : 10 })
```
