# 機能設計書 14-jest-mock

## 概要

本ドキュメントは、Jestテスティングフレームワークにおける`jest-mock`パッケージの機能設計を記述する。このパッケージは、モック関数の作成と管理、およびモジュール全体のモック化機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：ユニットテストにおいて、テスト対象のコードを依存関係から分離することは非常に重要である。`jest-mock`は、関数やモジュールを制御可能なモックオブジェクトに置き換えることで、テストの独立性と再現性を確保する。これにより、外部API呼び出し、データベースアクセス、ファイル操作などの副作用を持つ処理を安全にテストできる。

**機能の利用シーン**：
- `jest.fn()` - 関数のモック化
- `jest.mock()` - モジュール全体のモック化
- `jest.spyOn()` - 既存オブジェクトメソッドのスパイ化
- モック呼び出し履歴の追跡と検証

**主要な処理内容**：
1. `fn()` - モック関数の生成
2. `spyOn()` - 既存メソッドのスパイ化
3. `mocked()` - TypeScript型付けヘルパー
4. `replaceProperty()` - プロパティの一時的な置換
5. モック状態管理（calls, results, instances, contexts, lastCall）
6. モック実装の動的設定（mockImplementation, mockReturnValue等）

**関連システム・外部連携**：`jest-runtime`がモジュールモック機能を実現するためにこのパッケージを使用する。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Mock Functionsページ | 参照画面 | jest.fn(), jest.mock(), spyOn()の説明 |
| 20 | Jestオブジェクトページ | 参照画面 | jest.fn(), jest.mock()のAPI説明 |

## 機能種別

モック機能 / テストダブル / スパイ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| implementation | Function | No | モックの実装関数 | 関数型 |
| object | object | Yes (spyOn) | スパイ対象オブジェクト | オブジェクト |
| methodKey | string\|symbol | Yes (spyOn) | スパイ対象メソッド名 | 存在するプロパティ |
| accessType | 'get'\|'set' | No | アクセサスパイの種別 | 'get'または'set' |

### 入力データソース

テストコードからの直接呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| mock | Mock<T> | モック関数オブジェクト |
| mock.calls | Array | 呼び出し引数の配列 |
| mock.results | Array | 戻り値の配列 |
| mock.instances | Array | コンストラクタとして使用された際のインスタンス |
| mock.contexts | Array | 呼び出し時のthisコンテキスト |
| mock.lastCall | Array | 最後の呼び出し引数 |

### 出力先

呼び出し元のテストコード

## 処理フロー

### 処理シーケンス

```
1. fn()呼び出し
   └─ makeComponent()でモックメタデータ生成
2. createMockFunction()でProxyベースのモック関数生成
3. モック状態オブジェクト初期化（calls, results等）
4. mockImplementation等のヘルパーメソッド追加
5. モック関数の返却
```

### フローチャート

```mermaid
flowchart TD
    A[fn呼び出し] --> B[makeComponent]
    B --> C[createMockFunction]
    C --> D[モック状態初期化]
    D --> E[ヘルパーメソッド追加]
    E --> F[モック関数返却]

    G[spyOn呼び出し] --> H{アクセサ?}
    H -->|Yes| I[getter/setter取得]
    H -->|No| J[メソッド取得]
    I --> K[モック関数で置換]
    J --> K
    K --> L[mockRestore設定]
    L --> F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 呼び出し追跡 | 全ての呼び出しはmock.callsに記録 | モック関数呼び出し時 |
| BR-02 | 戻り値追跡 | 戻り値はmock.resultsにtype/valueで記録 | モック関数返却時 |
| BR-03 | 自動クリア | mockReset()でモック状態をリセット | 明示的呼び出し時 |
| BR-04 | 完全復元 | mockRestore()でオリジナル実装を復元 | spyOn使用時 |

### 計算ロジック

mock.calls.length: 呼び出し回数の取得
mock.results[n].type: 'return', 'throw', 'incomplete'のいずれか

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | Error | spyOn対象プロパティが存在しない | 存在するプロパティを指定 |
| N/A | Error | 設定不可能プロパティへのspyOn | 設定可能なプロパティを使用 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

Proxyを使用したモック実装により、オーバーヘッドを最小化。

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

特になし

## 備考

- ES6クラスのモック対応
- TypeScriptの型推論サポート（Mocked<T>型）
- 非同期関数のモック対応（mockResolvedValue/mockRejectedValue）

---

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

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

### 推奨読解順序

#### Step 1: 型定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/jest-mock/src/index.ts` | Mock型、MockInstance型、MockedFunction型 |

**主要処理フロー**:
- **45-69行目**: MockResult型定義（type: 'return'|'throw'|'incomplete'）
- **71-98行目**: MockState型定義（calls, instances, invocationCallOrder等）
- **100-159行目**: MockFunctionState型（calls, contexts, instances, lastCall, results）

**読解のコツ**: TypeScriptのジェネリクスを多用しているため、T（戻り値型）、Y（引数型配列）の関係を理解することが重要。

#### Step 2: ModuleMockerクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/jest-mock/src/index.ts` | ModuleMockerクラス |

**主要処理フロー**:
- **481-523行目**: ModuleMockerコンストラクタ、_environmentGlobal設定
- **526-560行目**: _getSlots() - オブジェクトのモック可能スロット取得
- **695-845行目**: fn()メソッド - モック関数の生成
- **847-1051行目**: spyOn()メソッド - メソッドスパイの生成

#### Step 3: モック関数の内部構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/jest-mock/src/index.ts` | _makeComponent, _createMockFunction |

**主要処理フロー**:
- **594-687行目**: _makeComponent - モックメタデータ生成
- **238-328行目**: createMockFunction（モジュールスコープ） - Proxyベースのモック関数実装
- **330-478行目**: ヘルパーメソッドの追加（mockReturnValue, mockImplementation等）

#### Step 4: spyOn実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/jest-mock/src/index.ts` | _spyOnProperty |

**主要処理フロー**:
- **1053-1141行目**: _spyOnProperty - プロパティへのスパイ
- **903-927行目**: spyOnでのアクセサ処理
- **929-996行目**: spyOnでの通常メソッド処理

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

```
ModuleMocker.fn() (index.ts:695)
    │
    ├─ _makeComponent (index.ts:594)
    │      └─ _getSlots (index.ts:526)
    │
    └─ createMockFunction (index.ts:238)
           │
           ├─ Proxy構築
           │
           ├─ mock.calls初期化
           │
           └─ ヘルパーメソッド追加
                  ├─ mockReturnValue
                  ├─ mockReturnValueOnce
                  ├─ mockResolvedValue
                  ├─ mockRejectedValue
                  ├─ mockImplementation
                  ├─ mockImplementationOnce
                  ├─ mockReturnThis
                  ├─ mockName
                  ├─ getMockName
                  ├─ mockClear
                  ├─ mockReset
                  └─ mockRestore

ModuleMocker.spyOn() (index.ts:847)
    │
    ├─ プロパティ記述子取得
    │
    ├─ _spyOnProperty (アクセサ) (index.ts:1053)
    │
    └─ fn()でモック生成
           └─ オリジナル実装の保存
```

### データフロー図

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

implementation? ──────▶ fn() ───────────────────────▶ Mock<T>
                          │
                          ├── Proxy生成
                          │
                          └── 状態オブジェクト初期化
                                 │
                                 ├── calls: []
                                 ├── results: []
                                 ├── instances: []
                                 ├── contexts: []
                                 └── lastCall: undefined

object, methodKey ────▶ spyOn() ─────────────────────▶ SpyInstance<T>
                          │
                          ├── オリジナル実装保存
                          │
                          └── fn()でモック置換
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-mock/src/index.ts` | ソース | 全実装（ModuleMocker, fn, spyOn等） |
