# 機能設計書 32-test

## 概要

本ドキュメントは、Node.js の `test` モジュール（組み込みテストランナー）の機能設計書である。このモジュールは、外部のテストフレームワークに依存せずにテストを作成・実行するための包括的なテスト機能を提供する。

### 本機能の処理概要

test モジュールは、Node.js 18以降で導入された組み込みテストランナーで、単体テスト・統合テストの作成と実行を可能にする。BDD/TDD 両方のスタイルでテストを記述でき、モック、スナップショットテスト、コードカバレッジなどの高度な機能も提供する。

**業務上の目的・背景**：これまでNode.jsでテストを行うには Jest、Mocha、AVA などの外部ライブラリが必要だったが、組み込みテストランナーの提供により、追加のセットアップなしでテストを開始できるようになった。開発者体験の向上と依存関係の削減が主な目的である。

**機能の利用シーン**：
- アプリケーションの単体テスト・統合テストの作成
- CI/CDパイプラインでの自動テスト実行
- TDD（テスト駆動開発）での開発フロー
- レガシーコードのリファクタリング時の回帰テスト
- APIやライブラリの動作確認

**主要な処理内容**：
1. テストケースの定義（`test()` / `it()`）とテストスイートの定義（`describe()` / `suite()`）
2. フック関数の実行（`before` / `after` / `beforeEach` / `afterEach`）
3. テストの実行とレポート生成（TAP、spec、dot、junit等）
4. モック機能（関数モック、タイマーモック、モジュールモック）
5. スナップショットテスト
6. コードカバレッジ計測

**関連システム・外部連携**：
- `assert` モジュール（アサーション）
- `async_hooks` モジュール（非同期コンテキスト追跡）
- V8 Coverage API（コードカバレッジ）
- 各種レポーター（TAP、spec、junit等）

**権限による制御**：特段の権限制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIツールのため画面なし |

## 機能種別

テスト実行 / レポート生成 / カバレッジ計測

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | No | テスト名 | - |
| options | object | No | テストオプション | オブジェクトであること |
| options.skip | boolean \| string | No | テストをスキップするか | - |
| options.todo | boolean \| string | No | TODOとしてマークするか | - |
| options.only | boolean | No | このテストのみ実行するか | - |
| options.timeout | number | No | タイムアウト（ミリ秒） | 正の整数 |
| options.concurrency | number | No | 同時実行数 | 正の整数 |
| fn | Function | No | テスト関数（同期/非同期） | 関数であること |

### 入力データソース

- テストコードファイル（`*.test.js`、`*.spec.js`等）
- コマンドライン引数（`--test-name-pattern`、`--test-only`等）
- 設定ファイル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | TestResult | テスト結果オブジェクト |
| duration_ms | number | テスト実行時間 |
| pass | boolean | テスト成功/失敗 |
| error | Error | 失敗時のエラー情報 |

### 出力先

- 標準出力（TAP形式、spec形式等）
- レポーターが指定したファイル（lcov、junit等）
- `test:pass`、`test:fail` 等のイベントストリーム

## 処理フロー

### 処理シーケンス

```
1. テストツリー構築
   └─ createTestTree() でルートテストを作成
2. テストファイル読み込み
   └─ Glob パターンでテストファイルを検出
3. テスト定義収集
   └─ test() / describe() 呼び出しでサブテストを登録
4. フック実行
   └─ before → beforeEach → test → afterEach → after
5. テスト実行
   └─ 各テスト関数を実行し結果を収集
6. レポート出力
   └─ 指定されたレポーターでフォーマット・出力
7. カバレッジ収集（オプション）
   └─ V8 Coverage API から情報取得
```

### フローチャート

```mermaid
flowchart TD
    A[テストファイル検出] --> B[createTestTree]
    B --> C[テスト定義収集]
    C --> D{サブテストあり?}
    D -->|Yes| E[before フック実行]
    E --> F[beforeEach フック実行]
    F --> G[テスト関数実行]
    G --> H{成功?}
    H -->|Yes| I[pass カウント]
    H -->|No| J[fail カウント]
    I --> K[afterEach フック実行]
    J --> K
    K --> L{次のテストあり?}
    L -->|Yes| F
    L -->|No| M[after フック実行]
    M --> N[レポート生成]
    N --> O{カバレッジ有効?}
    O -->|Yes| P[カバレッジ収集]
    O -->|No| Q[終了]
    P --> Q
    D -->|No| M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | テストスキップ | skip オプションが真の場合、テストは実行されない | テスト定義時 |
| BR-02 | only モード | --test-only 時は only マークのテストのみ実行 | CLI引数指定時 |
| BR-03 | タイムアウト | 指定時間を超えるとテスト失敗 | timeout 指定時 |
| BR-04 | 並行実行 | concurrency で指定した数のテストを同時実行可能 | concurrency 指定時 |
| BR-05 | フック順序 | before → beforeEach → test → afterEach → after の順で実行 | 常時 |

### 計算ロジック

特段の複雑な計算ロジックはなし。

## データベース操作仕様

該当なし

### 操作別データベース影響一覧

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR_TEST_FAILURE | Error | テストが失敗した場合 | テストコードを修正 |
| ERR_INVALID_ARG_TYPE | TypeError | 引数の型が不正 | 正しい型を渡す |
| ERR_INVALID_ARG_VALUE | Error | 引数の値が不正 | 有効な値を渡す |

### リトライ仕様

デフォルトではリトライは行わない。`--test-rerun-failures` オプションで失敗テストの再実行が可能。

## トランザクション仕様

該当なし

## パフォーマンス要件

- テストファイルの並行実行をサポート（`--test-concurrency`）
- ウォッチモード（`--watch`）でファイル変更時に自動再実行
- プロセス分離モードでテスト間の干渉を防止

## セキュリティ考慮事項

- テストコードは任意のコードを実行可能なため、信頼できるソースのみ実行すること
- モック機能は本番コードには使用しないこと

## 備考

- Node.js 18 で実験的機能として導入、Node.js 20 で安定版
- `node --test` コマンドでテストファイルを直接実行可能
- TAP（Test Anything Protocol）形式の出力をサポート

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、テストモジュールのエントリーポイントとエクスポートを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | test.js | `lib/test.js` | モジュールのエクスポート構造 |

**読解のコツ**: `test.js` は薄いラッパーで、実装の大部分は `internal/test_runner/` 配下にある。

#### Step 2: エントリーポイントを理解する

モジュールの主要エクスポートと遅延ロード構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | test.js | `lib/test.js` | module.exports の構造（11-22行目） |
| 2-2 | test.js | `lib/test.js` | mock の遅延ロード（26-38行目） |
| 2-3 | test.js | `lib/test.js` | snapshot の遅延ロード（43-62行目） |

**主要処理フロー**:
1. **8行目**: `internal/test_runner/harness` から test, suite, before, after 等をインポート
2. **9行目**: `internal/test_runner/runner` から run をインポート
3. **11-22行目**: module.exports で全APIをエクスポート

#### Step 3: ハーネスの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | harness.js | `lib/internal/test_runner/harness.js` | createTestTree 関数（43-113行目） |
| 3-2 | harness.js | `lib/internal/test_runner/harness.js` | runInParentContext 関数（361-392行目） |
| 3-3 | harness.js | `lib/internal/test_runner/harness.js` | hook 関数（394-405行目） |

**主要処理フロー**:
- **43-113行目**: テストツリーの構築、harness オブジェクトの初期化
- **104-109行目**: グローバルルートテストの作成
- **229-308行目**: プロセス状態のセットアップ（uncaughtException、unhandledRejection ハンドラ）
- **361-392行目**: test() / describe() の実装（runInParentContext）

#### Step 4: テストランナーの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | runner.js | `lib/internal/test_runner/runner.js` | createTestFileList 関数（120-138行目） |
| 4-2 | runner.js | `lib/internal/test_runner/runner.js` | run 関数のエクスポート |

**主要処理フロー**:
- **120-138行目**: Glob パターンによるテストファイル検出
- **145-200行目**: 子プロセス実行引数の構築

#### Step 5: モック機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | mock.js | `lib/internal/test_runner/mock/mock.js` | MockTracker クラス |
| 5-2 | mock_timers.js | `lib/internal/test_runner/mock/mock_timers.js` | タイマーモック |

### プログラム呼び出し階層図

```
test(name, options, fn)
    │
    ├─ runInParentContext(Test)
    │      ├─ testResources.get(executionAsyncId())
    │      │      └─ 親テスト取得 or lazyBootstrapRoot()
    │      │
    │      └─ parent.createSubtest(Factory, name, options, fn)
    │             └─ new Test({ parent, name, fn, ... })
    │
    └─ startSubtestAfterBootstrap(subtest)
           ├─ await bootstrapPromise
           ├─ await buildPromise
           └─ subtest.start()
                  └─ テスト実行 → 結果収集

describe(name, options, fn) / suite()
    │
    └─ runInParentContext(Suite)
           └─ parent.createSubtest(Suite, ...)
                  └─ new Suite({ parent, ... })

before(fn, options)
    │
    └─ hook('before')
           └─ parent.createHook('before', fn, options)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

テストファイル ─────────▶ Glob検出 ─────────▶ ファイルリスト
      │
      ▼
test() 呼び出し ────────▶ Test インスタンス作成 ──▶ テストツリー
      │
      ▼
テスト実行 ─────────────▶ fn() 実行 ─────────▶ 結果収集
      │
      ▼
レポート生成 ───────────▶ Reporter ──────────▶ 標準出力/ファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| test.js | `lib/test.js` | ソース | メインエントリーポイント |
| harness.js | `lib/internal/test_runner/harness.js` | ソース | テストハーネス・ツリー管理 |
| runner.js | `lib/internal/test_runner/runner.js` | ソース | テストファイル検出・実行管理 |
| test.js | `lib/internal/test_runner/test.js` | ソース | Test/Suite クラス定義 |
| mock.js | `lib/internal/test_runner/mock/mock.js` | ソース | モック機能 |
| mock_timers.js | `lib/internal/test_runner/mock/mock_timers.js` | ソース | タイマーモック |
| snapshot.js | `lib/internal/test_runner/snapshot.js` | ソース | スナップショットテスト |
| coverage.js | `lib/internal/test_runner/coverage.js` | ソース | コードカバレッジ |
| spec.js | `lib/internal/test_runner/reporter/spec.js` | ソース | spec レポーター |
| tap.js | `lib/internal/test_runner/reporter/tap.js` | ソース | TAP レポーター |
| junit.js | `lib/internal/test_runner/reporter/junit.js` | ソース | JUnit レポーター |
| utils.js | `lib/internal/test_runner/utils.js` | ソース | ユーティリティ関数 |
