--- name: typescript-testing description: リポジトリに即したTypeScriptテスト設計、振る舞いの証明、独立性、モック境界の基準を適用する。ユニットテストの作成・レビュー時に使用。 --- # TypeScript テストルール ## 前提条件の検出 フレームワークやコマンドを選択する前に、`package.json`、ロックファイル、テスト設定、既存テストのimportを確認する。Vitest固有のルールはVitestが設定されている場合にのみ適用する。それ以外は、以下の振る舞い、独立性、エビデンスに関するルールを維持しつつ、リポジトリで設定済みのTypeScriptテストハーネスを使用する。実行可能なハーネスを特定できない場合は、確認したパスと不足しているコマンドまたは設定を報告する。 ## テストフレームワーク - **Vitest**: リポジトリ設定または既存テストで選択されている場合に使用 - テストのインポート: `import { describe, it, expect, beforeEach, vi } from 'vitest'` - モックの作成: `vi.mock()` を使用 ## テストの基本方針 ### 品質要件 - **リグレッション保護**: クリティカルパス、ビジネスロジック、リグレッションが問題になる振る舞いにテストを集中させる。未検証の振る舞いが実害のあるリグレッションリスクを生む場合にテストを追加する - **独立性**: 各テストは他のテストに依存せず実行可能 - **再現性**: 時刻、乱数、環境値、外部I/Oを制御し、同一の入力から同一の観測可能な結果を得る - **可読性**: 各テストは1つの振る舞いを名前で示し、setup・action・assertionを分け、その振る舞いで使用する値だけをfixtureに含める ### テストの種類と範囲 1. **単体テスト(Unit Tests)** - 個々の関数やクラスの動作を検証 - 外部依存はすべてモック化 - 最も数が多く、細かい粒度で実施 2. **統合テスト(Integration Tests)** - 複数のコンポーネントの連携を検証 - テスト対象の振る舞いを構成するin-processコンポーネントは実物を使用。外部I/Oはモック範囲の判断を参照 - 主要な受け入れ基準を実装するフロー、またはin-processコンポーネントの境界をまたぐフローを検証 3. **機能横断検証** - 新機能が共有の統合ポイントに及び、その失敗が主要なユーザージャーニーまたは公開契約を壊す、あるいは副次的な観測可能な振る舞いを劣化させる場合は、既存機能の継続性を証明義務とする。その失敗を露出できる最も安い境界で証明する - 検証パターン: 既存機能動作 → 新機能有効化 → 既存機能の継続性確認 - 判定基準: 元の受け入れ基準で指定されたレスポンスフィールドと観測可能な振る舞いを維持する。処理時間のしきい値は、要件またはプロジェクト設定で値と計測方法が定義されている場合にのみ適用する - CI/CDでの自動実行を前提とした設計 ## テストの実装規約 ### ディレクトリ構造と命名規則 - テストは対象モジュールと同じ階層の `__tests__/` に置く - テストファイル: `{target-file-name}.test.ts` - 統合テストファイル: `{target-file-name}.int.test.ts` - テストスイート: 対象の機能や状況を説明する名前 - テストケース: 期待される動作を説明する名前 ### テストコードの品質ルール コミットするテストはすべて有効に保つ。現行の振る舞いを保護するテストは修復する。テストを削除するのは、対象の振る舞いが不要になったことを元の要件または実装契約で確認できる場合に限る。 ## テスト品質基準 ### 境界値・異常系の網羅 正常系に加え、境界値と異常系を含める。 ### 期待値の直接記述 期待値は実装上の計算から独立させる。契約の値をリテラルとして直接記述するか、独立した正規のfixtureまたは仕様から取得する。テスト対象と同じ定数や計算式から算出した期待値は、両方が誤っていても通過する。モックが入力を供給する場合、実装がそれを変換する箇所では期待値をモックの戻り値と異なる値にする。 ### 結果ベースの検証 呼び出し順序・回数ではなく結果を検証。 ### 意味あるアサーション 各テストは、値が返ったことではなく、利用側が依存するプロパティと、操作後の状態を検証する。 ### Capability Probeの事後条件 「動作するか」を確かめるprobeが成立するのは、利用側の境界を通り、利用側が必要とするプロパティそのものを検証している場合に限る。 コマンドの終了ステータス、importの成功、オブジェクトの存在は、対象に到達できることを示すので、probeの前提条件として扱い、利用側に必要なプロパティをアサーションに置く。 | probeの意図 | 前提条件(単独では不十分) | 代わりに検証する対象 | |---|---|---| | モジュールが使用可能 | `import` が解決する、`expect(mod).toBeDefined()` | 利用側のエントリポイント経由でexportされた関数を呼び、その戻り値または効果を検証 | | コマンドが動作する | 終了コード0 | 呼び出し側が利用する出力、ファイル、状態変化 | | 設定が適用されている | 設定ファイルがパースできる | その設定が変えるはずの観測可能な振る舞い | | migrationが実行された | コマンドが成功を報告した | 実エンジン経由のqueryがmigration後の形を返すこと | ### モック範囲の判断 連携がテスト対象となるin-processコンポーネントにはすべて実物を使用する。上位層の振る舞いをテストする場合は、直接依存する外部I/Oを代替する。外部アダプター、query、migration、service契約自体がテスト対象の場合は、実エンジンまたは本番相当のテストインスタンスを使用する。代替する場合も、テスト対象が送るrequestと受け入れるresponseの形を検証し、境界の契約が未検証にならないようにする。 ### Property-based Testing(fast-check) Design DocのACにProperty注釈が付与されている場合、`fc.assert(fc.property(...))` の形式でfast-checkを使用する。 ## モックの型安全性 モックには、テスト対象が実際に利用する範囲だけを型付けする(`Pick`)。インターフェース全体を型付けしないことで、使用していないメソッドの形が変わってもテストは壊れず、利用しているメソッドの変更では壊れる。モックのオブジェクトリテラルはその抽出済み型に対して `satisfies` で制約し、余分なプロパティや誤った名前をコンパイル時に落とす。 ## データ層テスト ### モックが検証できないもの モックは呼び出しパターンを検証するため、データ層の以下のプロパティはモックのみのテストでは検出されずに通過する: - スキーマの不一致(テーブル名、カラム名、データ型) - クエリの正確性(JOIN、フィルタ、集約、グルーピング) - データベース制約(NOT NULL、UNIQUE、外部キー) - マイグレーション互換性(スキーマ変更によるコードとの不整合) **振り分けルール**: 上記のプロパティがテスト対象の場合 — repositoryやデータアクセス実装自体を含む — 下記の優先順位に従って実エンジンに対して検証する。データアクセスがテスト対象ではなく依存先である場合はモックが正しい選択で、データ層からデータを受け取るビジネスロジック(repositoryをモック、serviceをテスト)、エラーハンドリングパス(接続失敗、タイムアウト)、データ層がテスト対象でないユニットテストが該当する。 ### 実データベーステスト(環境依存) 実データベースエンジンに対するデータ層の正確性を検証するオプション: - CI環境向けの**コンテナ化されたデータベース** - 高速フィードバック用の**インメモリデータベース**(注: dialect差異が問題を隠す場合がある) - seed data付きの**専用テストデータベース** リポジトリの根拠に合う最初の選択肢を使用する: 1. CI用のデータベースハーネスが設定されている場合は、それを使用する。 2. それ以外でcontainerを実行できる場合は、同じデータベースエンジンをcontainerで使用する。 3. 検証対象の振る舞いがdialect非依存の場合にのみin-memory databaseを使用し、未検証となるdialect固有の振る舞いを記録する。 4. リポジトリですでに専用テストデータベースのprovisioningと分離が行われている場合は、それを使用する。 いずれも利用できず、データ層の正確性がテスト対象である場合は作業を止め、不足している環境前提条件を報告する。モックだけの結果は、query、schema、constraint、migrationの正確性を示すエビデンスにはならない。 ### AI生成コードとスキーマ認識 生成されたデータアクセスコードは、構文が正しくても存在しないスキーマ要素を参照しうるうえ、モックベースのテストはどちらでもパスする。そのためDesign Docに明示的なスキーマ参照を含め、レビュー時にドキュメント化されたスキーマとデータアクセスコードを照合できるようにする。