# テストポリシー

以下の5条件の証拠ゲートを満たす場合に限り、観測可能な振る舞いの変更には既存テストでは検出できない回帰を防ぐ最小限のテストが必要であり、バグ修正には変更前の失敗を検出できる既存または新規のリグレッションテストが必要。

## 原則

| 原則 | 基準 |
|------|------|
| Given-When-Then | テストは3段階で構造化する |
| 1テスト1概念 | 複数の関心事を1テストに混ぜない |
| 振る舞いを検証 | 実装の詳細ではなく振る舞いをテストする |
| 内部構造を契約化しない | 行数・ソース文言・import・helper名・ファイル配置・runtime freeze・参照同一性を、観測可能な契約の代用品にしない |
| 独立性 | 他のテストや実行順序に依存しない |
| 再現性 | 時間やランダム性に依存せず、毎回同じ結果 |
| 非実行資産を固定しない | 実行時の振る舞いを定義しない本文や章構成を CI の失敗条件にしない |
| 配布定義を複製しない | 同梱された宣言的資産の具体的な定義をテストの期待値へ丸写ししない |
| 否定契約を観測単位で検証 | 禁止・拒否・非継承・非対応は、完全一致文字列の不在だけで合格にしない |
| モック契約一致 | 外部SDK・APIのモックは実契約と一致させ、誤った前提をテストで固定しない |

内部構造だけを固定する既存テストは、別の内部構造テストへ置換せず削除する。削除対象の確認は変更契約の所有者を import・呼び出し・直接参照する既存テスト群に限定し、ファイル名やテストが名乗る目的では除外せず、無関係なテスト群の全体掃除へ広げない。実在する外部契約や回帰リスクがある場合だけ、その観測可能な振る舞いを検証する。文字列自体が CLI、プロトコル、公開エラーコードなどの契約である場合は完全一致を使用できる。

テスト追加を必須にできるのは、今回その検証を追加する義務（明示的な検証要件、検証対象の振る舞い・条件自体の追加や変更、または確認済み不具合）、正本となる受入条件または観測可能な契約、実在する経路で起こり得る具体的な失敗、既存テストではその失敗を検出できない根拠、検証を所有する最小レイヤーを全て示せる場合に限る。いずれかを示せない場合はテスト追加を要求しない。モジュール数、呼び出しチェーンの長さ、内部分岐数、ファイル配置だけを追加根拠にしてはならない。同じ失敗を既存の unit、integration、E2E のいずれかで検出できる場合、別レイヤーや consumer ごとの重複テストを追加しない。

検証追加の義務は、観測しようとする契約の所有者と条件に対して判断する。新しい利用側が既存部品を呼ぶことは、利用側の受け渡しや結果を検証する根拠にはなるが、未変更の既存部品内部の別条件を新たに検証する義務にはならない。要件に既存契約の維持が書かれているだけでは、既存のテスト不足を今回の必須修正へ移さない。明示的なテスト要求、変更された受け渡し・更新規則、または確認済みの不整合がある場合は、その対象条件を検証する。

## カバレッジ基準

| 対象 | 基準 |
|------|------|
| 新しい観測可能な振る舞い | 既存テストで回帰を検出できない場合、契約所有者の最小レイヤーにテスト必須 |
| バグ修正 | 変更前の失敗を検出できる既存または新規のリグレッションテスト必須。既存テストで十分なら追加不要 |
| 観測可能な振る舞いの変更 | 既存テストが旧契約だけを検証する場合は更新必須。既存テストが新契約も検出できるなら追加・更新不要 |
| 副作用・状態遷移の変更 | 契約に関係する正常系と代表的な失敗経路がどのレイヤーでも未検証なら REJECT |
| 共通化・抽象化による契約変更 | 同じ契約を持つ既存分岐の回帰を既存証跡で検出できなければ、所有者で代表的に検証 |
| パーサー・設定境界の変更 | 変更した契約に関係し、既存テストで未検出の入力分類だけを検証 |
| ビルド（型チェック） | ビルド成功必須。失敗は REJECT |
| エッジケース・境界値 | テスト推奨（Warning） |

## テスト優先度

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

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

## 非実行資産のテスト

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

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

## 自然言語・宣言的資産のテスト

プロンプト、instruction、自然言語の判定条件、ワークフローなどの宣言的定義は実行時入力である。自然言語の文字列固定と配布定義の複製を振る舞いの回帰テストとして扱わず、対象ごとに適切な検証レイヤーを選ぶ。

| 基準 | 判定 |
|------|------|
| `toContain` や全文一致だけで、プロンプトが意図した分類・判断を行うと主張する | REJECT |
| condition の説明文を完全一致で固定し、表現変更だけで失敗する | REJECT |
| parser・loader 自体の構造契約を、必要最小限の専用 fixture で検証する | OK |
| 配布される宣言的資産を全件 load し、schema 適合を確認する smoke test | OK |
| 個別の配布資産から step 名、rule、遷移先、設定値を期待値へ複製し、定義差分だけを検出する | REJECT |
| 状態遷移や副作用を、代表的な最小シナリオの実行結果で検証する | OK |
| 意味判定を決定的なコードへ分離し、入力と結果を境界ケースで検証する | OK |
| モデルの判断品質をシナリオ評価で検証し、通常の決定的テストと区別する | OK |
| CLI出力、プロトコル値、エラーコードなど、文字列自体が外部公開契約であるため完全一致を検証する | OK |

## 置換された旧仕様のテスト

仕様変更で旧設計の構成要素（UI・API・イベント・状態・ラベルなど）が新設計に置き換えられた場合、テストは新仕様の振る舞いを肯定的に検証する。旧仕様が存在しないことだけを固定しない。

| 基準 | 判定 |
|------|------|
| 旧仕様の構成要素が存在・呼び出しされないことだけを検証するテストを追加 | REJECT |
| 責務を失った実装単位に、旧仕様由来の不在確認テストを残す | REJECT |
| 本番コードが最終的に変更されていないファイルで、旧仕様否定のためだけにテストを変更 | REJECT |
| 新仕様を、新しい責務を持つ層（上位モジュール・サービス・統合フロー等）で肯定的に検証 | OK |
| 旧仕様削除で未使用になったテストを削除し、新仕様の回帰テストへ置き換える | OK |

## テスト品質

| 観点 | 良い | 悪い |
|------|------|------|
| 独立性 | 他のテストに依存しない | 実行順序に依存 |
| 型安全 | コードはビルド（型チェック）が通ること |
| 再現性 | 毎回同じ結果 | 時間やランダム性に依存 |
| 明確性 | 失敗時に原因が分かる | 失敗しても原因不明 |
| 焦点 | 1テスト1概念 | 複数の関心事が混在 |
| UI要素の同定 | role と accessible name の完全一致など、利用者が観測する契約で特定する | 広い部分一致、最初の一致、表示位置で対象を特定する |
| 衝突ケース | 同じ表示文字列を持つ有効なデータでも対象を区別できることを検証する | フィクスチャの表示値を意図的にすべて異ならせ、衝突の可能性を隠す |

## assertion の契約

各 assertion を、正本が明示した不変条件、正本から不可欠に直接導出して由来と理由を記録した不変条件、または変更対象外の観測可能な既存契約へ対応付ける。対応先を示せない assertion は追加しない。

| 基準 | 判定 |
|------|------|
| 正本が値の等価性だけを定める一方で、参照同一性やコピーの有無もテストで固定 | REJECT |
| 正本が拒否だけを定める一方で、正本にないエラー型や文言もテストで固定 | REJECT |
| 防御的実装から入力分類、振る舞い、エラー種別、文言、内部表現を推測し、新しいテスト契約として固定 | REJECT |
| 全 assertion が、明示または不可欠な不変条件か、保存対象の観測可能な契約へ対応 | OK |

## 副作用・状態遷移のテスト

副作用や状態遷移を伴う変更は、正常系の成功だけでは十分に検証されたとは扱わない。

| 基準 | 判定 |
|------|------|
| 正常系のみで、失敗・中断・早期終了時の状態をどのレイヤーでも検証していない | REJECT |
| 取得・開始・登録・反映した状態の後片付けや重複実行を検証していない | REJECT |
| 共有状態や後続処理に影響する変更で、途中失敗後の再実行を検証していない | 警告。影響が主要経路なら REJECT |
| モックで確認した範囲と未確認の実連携範囲を区別していない | 警告。主要要件なら REJECT |
| 下位レイヤー（ユニットテスト等）で検証済みの失敗経路を上位レイヤー（統合テスト等）で重複テストしている | 警告。プロジェクトのテスト方針で明示的に禁止されている場合は REJECT |
| 正常系、代表的な失敗経路、境界的な状態遷移をそれぞれ検証している | OK |
| 失敗経路が適切なレイヤー（ユニットテスト等）で検証済みであり、上位レイヤーでは正常系の結合のみ検証している | OK |

要求が、特定の変化（入力、環境、設定、接続状態などの変化）を名指しし、その変化の後もその時点の状態に合わせて振る舞うことを求め、かつその変化をまたいで同じ実体（画面、プロセス、接続、セッション、キャッシュなど）が存続する契約では、次の基準を適用する。既存の振る舞いを維持する要求や、変化を名指ししない要求は、それだけではこの契約にならない。

| 基準 | 判定 |
|------|------|
| 変化の前後を同じ実体で一続きに観測せず、条件だけ変えた別の実体を作り直した観測をその契約の証拠にする | REJECT |
| 変化前にその実体が作った成果物（出力済みの表示、計算済みの結果、保持している一覧や接続状態など）を追従の対象から外し、変化後に新しく作るものだけを対象にして、変化前の成果物を旧状態のまま残す読み替えで契約を満たしたとする | REJECT |
| 同じ実体をテストで観測できるのに、実環境での手動確認だけへ送る | REJECT |
| 状態・所有権や連続実行・所有権・並行性の確認を、該当する契約なのに「該当なし」として扱う | REJECT |
| 同じ実体で変化前の状態 → 変化 → 変化後の状態を一続きに観測している | OK |

## 契約変更と既存分岐のテスト

共通ヘルパー、正規化関数、builder、adapter などで契約を統一する変更では、同じ契約を持つ既存分岐が保護されていることを確認する。consumer ごとにテストを複製せず、契約所有者の unit test、代表入力の parameterized test、または既存の上位 behavior test のうち、同じ故障を最小に検出できる証跡を使う。

| 基準 | 判定 |
|------|------|
| 既存の同契約分岐に具体的な回帰経路があり、どの既存テストでも検出できない | REJECT |
| モジュール数、consumer 数、return / throw / catch / early return の数だけを根拠にテストを要求 | REJECT |
| 同じ故障を検出するassertをconsumerごと、または複数レイヤーへ複製 | REJECT |
| 契約所有者のテストまたは既存の上位behavior testが、同契約分岐の代表的な故障を検出 | OK |

## 契約テストの十分性

新しい設定値、実行時に選択される能力・バックエンド・オプション、権限、出力契約を追加・変更した場合、テストは値の存在ではなく、契約を変える分岐条件を踏んでいることを確認する。

| 基準 | 判定 |
|------|------|
| 新しいオプションの正常系だけを確認している | 警告 |
| 未設定、設定、不正値、継承、非継承、override、非対応対象のうち、要件に関係する分岐を検証していない | REJECT |
| 利用者向けの表示・検証用入口で、主実行経路と同じ契約になることを確認していない | REJECT |
| 値の表示だけを確認し、実行時に使われる解決入力と一致するか検証していない | REJECT |
| 漏れ検証を完全一致文字列だけで行い、順序、大小文字、空白、部分漏れの違いを見逃す | REJECT |
| 空文字列、空白だけの文字列、空配列、大小文字違いなど、設定境界で正規化対象になり得る値を検証していない | 警告。契約の主要分岐なら REJECT |
| 契約所有者の観測可能な境界で、要件に関係する正常系、拒否系、非継承系を確認している | OK。各hopの重複検証は不要 |

## 否定・非継承・拒否のテスト

禁止、拒否、非継承、非対応、隔離を検証するテストは、出力全体に特定の文字列が存在しないことだけを根拠にしない。
観測対象の行、レコード、フィールド、呼び出し引数などを抽出し、禁止値が順序、大小文字、空白、区切り、部分一致の形でも漏れていないことを検証する。

| 基準 | 判定 |
|------|------|
| 完全一致文字列の不在だけで、禁止値や非継承値が使われていないと判断している | REJECT |
| 許可値の存在確認だけで、拒否値・禁止値・非継承値が末端に渡らないことを検証していない | REJECT |
| 出力行、イベント、レコード、フィールド、呼び出し引数などの観測単位を抽出し、禁止値ごとに検証している | OK |
| 許可側と拒否側、継承側と非継承側を対にして検証している | OK |
| 新しい E2E の timeout、cleanup、強制終了の扱いが既存の同種テスト規約と一致していない | 警告。プロセスリークやフレークの原因になる場合は REJECT |

## パーサー・設定境界のテスト

外部ファイル、設定、YAML/JSON、CLI入力などを読む境界では、変更した契約に関係する入力分類を検証する。一般的な異常入力一覧を全parserへ機械的に適用しない。

| 基準 | 判定 |
|------|------|
| 変更した入力契約に object/null/欠落の扱いが含まれ、具体的な未検出failureがある | 該当する代表入力のテスト必須 |
| parser変更だけを理由に object/null/欠落の全組合せを要求 | REJECT |
| ファイル種別やアクセス失敗が変更契約に関係しないのに、通常ファイル・壊れたリンク・権限エラーを一律要求 | REJECT |
| ユーザーやマシンの既存設定を引き継ぐ可能性があるテストで、空の設定ディレクトリや一時HOMEに隔離していない | REJECT |
| 変更契約に関係する代表的な契約外shapeを、無視・正規化・明示エラーのいずれかに固定している | OK |

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

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

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

## 契約モックとテストダブル

外部SDK、外部API、生成クライアント、CLI をモックする場合、モックの例外型、ステータス、戻り値、欠落値、部分成功、冪等性は実契約に合わせる。内部の builder、runner、adapter をテストダブルにする場合も、権限、能力、override、欠落値、副作用の意味契約を本実装に合わせる。型が合うだけでは、意味契約を検証したことにならない。

| 基準 | 判定 |
|------|------|
| 公式仕様、SDK型、生成スキーマ、既存同種テストのいずれかと突合してモック値を決めている | OK |
| 実装が期待する例外や戻り値をモックに投げさせ、そのテスト成功を外部契約の根拠にしている | REJECT |
| 同じサービス内の別操作のエラー型やレスポンス shape を流用している | REJECT |
| 型安全なモックだが、操作ごとの意味契約（既存リソース時、部分成功時、未検出時など）を確認していない | REJECT |
| 内部テストダブルが、本実装で必ず渡る制約・override・副作用を落としている | REJECT |
| 実連携をスタブ化する場合、モックで確認した範囲と未確認の実連携範囲をレポートに分けて記録している | OK |

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

画面の初期取得がある場合、関係ない再描画やloading切替で同じ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テストしかない場合、ユニットテストの追加を提案する
- プロジェクト固有のテスト方針（AGENTS.md、CLAUDE.md 等）がテストレイヤーの責務を定義している場合、そちらを優先する。汎用ポリシーの「失敗経路必須」は「どのレイヤーにも存在しない場合」に適用し、適切なレイヤーで検証済みの経路を上位レイヤーで重複させない

### インテグレーションテストの選択

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

| 条件 | 判定 |
|------|------|
| データフローが複数モジュールを横断するという事実だけ | 追加根拠にしない。モジュール数で判定しない |
| 契約所有者のunit testが同じ故障を検出できる | unit testで十分 |
| 境界間の誤配線・変換漏れをunit testでは検出できない具体的な経路がある | その結合を通る最小のintegration test必須 |
| 新しい状態が既存workflowへ合流する | 遷移判断は所有者で検証。組み込みでしか起きない故障がある場合だけ代表フローをintegrationで検証 |
| 新しいoptionが呼び出しチェーンを伝搬する | 解決所有者と、既存テストで未検出になる最小handoffを検証。各hopやconsumerで繰り返さない |
| 実行時構成が選択・優先順位を決める | 限定構成では隠れる具体的な誤配線がある場合だけ、本番相当構成のintegration testを要求 |
| 既存の上位behavior testが同じ結合故障を検出できる | 追加不要。重複integration testは要求しない |

## ユニットテスト基準

| 基準 | 判定 |
|------|------|
| テスト対象の内部実装をモックする（振る舞いではなく実装を検証） | 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 === 'embedded' ? 1 : 10 })
```

## e2e-testing 判定基準

### E2Eテストのスコープ

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

### 振る舞い観測

| 基準 | 判定 |
|------|------|
| ユーザー操作や外部入力に対する結果が観測されている | OK |
| 拒否・エラー・リカバリーの経路で期待する結果が確認されている | OK |
| 設定や内部状態だけを確認してユーザー-visible な結果を見ていない | REJECT |
| 実外部環境に依存する確認しかなく、主要境界の deterministic test がない | 警告またはREJECT |

### 否定契約の観測

| 基準 | 判定 |
|------|------|
| 「特定の一文が出ていない」だけで拒否・非継承・隔離を検証した扱いにする | REJECT |
| 許可された値の表示だけを確認し、禁止値が末端処理へ渡っていないことを見ていない | REJECT |
| 観測単位を抽出して、禁止値・拒否値・非継承値が含まれないことを値単位で確認する | OK |
| 同じシナリオで許可側と拒否側、継承側と非継承側を比較できる | OK |

## unit-testing 判定基準

### 振る舞い保証

| 基準 | 判定 |
|------|------|
| 期待する戻り値・例外・副作用が直接検証されている | OK |
| 境界変更の成功/失敗、許可/拒否の両側が検証されている | OK |
| 対象操作を通さず設定値・内部状態を読むだけ、または別の作用が契約なのに内部状態で代用している | REJECT |
| 状態の遷移・保持が契約であり、対象操作と条件を通してその結果を確認している | OK。状態の観測であることだけを理由に別の観測方法を必須にしない |
| 外部環境がないと主要な境界条件を再現できない | Fake や Stub による deterministic test を検討 |
