| name | typescript-testing |
| description | リポジトリに即したTypeScriptテスト設計、振る舞いの証明、独立性、モック境界の基準を適用する。ユニットテストの作成・レビュー時に使用。 |
TypeScript テストルール
前提条件の検出
フレームワークやコマンドを選択する前に、package.json、ロックファイル、テスト設定、既存テストのimportを確認する。Vitest固有のルールはVitestが設定されている場合にのみ適用する。それ以外は、以下の振る舞い、独立性、証跡に関するルールを維持しつつ、リポジトリで設定済みのTypeScriptテストハーネスを使用する。実行可能なハーネスを特定できない場合は、確認したパスと不足しているコマンドまたは設定を報告する。
テストフレームワーク
- Vitest: リポジトリ設定または既存テストで選択されている場合に使用
- テストのインポート:
import { describe, it, expect, beforeEach, vi } from 'vitest'
- モックの作成:
vi.mock() を使用
テストの基本方針
品質要件
- カバレッジ: カバレッジは目標ではなく未テスト領域を見つける診断シグナルとして扱う(目標化すると自明なテストに歪む — グッドハートの法則)。クリティカルパス・ビジネスロジック・リグレッションが問題になる振る舞いにテストを集中させる。パーセンテージ達成のためではなく、ギャップが実際のリグレッションを無防備にしている箇所でカバレッジを上げる。数値しきい値はプロジェクトの CI 設定に委ねる
- 独立性: 各テストは他のテストに依存せず実行可能
- 再現性: 時刻、乱数、環境値、外部I/Oを制御し、同一の入力から同一の観測可能な結果を得る
- 可読性: 各テストは1つの振る舞いを名前で示し、setup・action・assertionを分け、その振る舞いで使用する値だけをfixtureに含める
テストの種類と範囲
-
単体テスト(Unit Tests)
- 個々の関数やクラスの動作を検証
- 外部依存はすべてモック化
- 最も数が多く、細かい粒度で実施
-
統合テスト(Integration Tests)
- 複数のコンポーネントの連携を検証
- テスト対象の振る舞いを構成するin-processコンポーネントは実物を使用。外部I/Oはモック範囲の判断を参照
- 主要な受け入れ基準を実装するフロー、またはin-processコンポーネントの境界をまたぐフローを検証
-
E2Eテストでの機能横断検証
- 新機能追加時、既存機能への影響を必ず検証
- 各統合ポイントを分類する: 失敗が主要なユーザージャーニーまたは公開契約を壊すなら「高」、副次的な観測可能な振る舞いを劣化させるなら「中」。「高」と「中」をカバーする
- 検証パターン: 既存機能動作 → 新機能有効化 → 既存機能の継続性確認
- 判定基準: 元の受け入れ基準で指定されたレスポンスフィールドと観測可能な振る舞いを維持する。処理時間のしきい値は、要件またはプロジェクト設定で値と計測方法が定義されている場合にのみ適用する
- CI/CDでの自動実行を前提とした設計
テストの実装規約
ディレクトリ構造と命名規則
- テストは対象モジュールと同じ階層の
__tests__/ に置く
- テストファイル:
{対象ファイル名}.test.ts
- 統合テストファイル:
{対象ファイル名}.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<T, '使用するメソッド'>)。インターフェース全体を型付けしないことで、使用していないメソッドの形が変わってもテストは壊れず、消費しているメソッドの変更では壊れる。モックのオブジェクトリテラルはその抽出済み型に対して satisfies で制約し、余分なプロパティや誤った名前をコンパイル時に落とす。
データ層テスト
モックが検証できないもの
モックは呼び出しパターンを検証するため、データ層の以下のプロパティはモックのみのテストでは検出されずに通過する:
- スキーマの不一致(テーブル名、カラム名、データ型)
- クエリの正確性(JOIN、フィルタ、集約、グルーピング)
- データベース制約(NOT NULL、UNIQUE、外部キー)
- マイグレーション互換性(スキーマ変更によるコードとの不整合)
振り分けルール: 上記のプロパティがテスト対象の場合 — repositoryやデータアクセス実装自体を含む — 下記のラダーに従って実エンジンに対して検証する。データアクセスがテスト対象ではなく依存先である場合はモックが正しい選択で、データ層からデータを受け取るビジネスロジック(repositoryをモック、serviceをテスト)、エラーハンドリングパス(接続失敗、タイムアウト)、データ層がテスト対象でないユニットテストが該当する。
実データベーステスト(環境依存)
実データベースエンジンに対するデータ層の正確性を検証するオプション:
- CI環境向けのコンテナ化されたデータベース
- 高速フィードバック用のインメモリデータベース(注: dialect差異が問題を隠す場合がある)
- seed data付きの専用テストデータベース
リポジトリの根拠に合う最初の選択肢を使用する:
- CI用のデータベースハーネスが設定されている場合は、それを使用する。
- それ以外でcontainerを実行できる場合は、同じデータベースエンジンをcontainerで使用する。
- 検証対象の振る舞いがdialect非依存の場合にのみin-memory databaseを使用し、未検証となるdialect固有の振る舞いを記録する。
- リポジトリですでに専用テストデータベースのprovisioningと分離が行われている場合は、それを使用する。
いずれも利用できず、データ層の正確性がテスト対象である場合は作業を止め、不足している環境前提条件を報告する。モックだけの結果は、query、schema、constraint、migrationの正確性を示す証跡にはならない。
AI生成コードとスキーマ認識
生成されたデータアクセスコードは、構文が正しくても存在しないスキーマ要素を参照しうるうえ、モックベースのテストはどちらでもパスする。そのためDesign Docに明示的なスキーマ参照を含め、レビュー時にドキュメント化されたスキーマとデータアクセスコードを照合できるようにする。