# 機能設計書 15-@jest/fake-timers

## 概要

本ドキュメントは、Jestテスティングフレームワークにおける`@jest/fake-timers`パッケージの機能設計を記述する。このパッケージは、setTimeout、setInterval、Dateなどのタイマー関連APIを制御可能なモックに置き換えるフェイクタイマー機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：非同期処理やタイムアウトに依存するコードのテストは、実際の時間経過を待つと遅くなり、タイミングに依存するため不安定になりがちである。`@jest/fake-timers`は、時間の進行をプログラムで制御することで、テストの高速化と決定性の確保を実現する。

**機能の利用シーン**：
- setTimeout/setInterval を使用したコードのテスト
- Date.now() に依存するコードのテスト
- requestAnimationFrame を使用したアニメーションコードのテスト
- デバウンス/スロットル処理のテスト

**主要な処理内容**：
1. `useFakeTimers()` - フェイクタイマーの有効化
2. `useRealTimers()` - リアルタイマーへの復元
3. `advanceTimersByTime()` - 指定ミリ秒分のタイマー進行
4. `advanceTimersToNextTimer()` - 次のタイマーまで進行
5. `runAllTimers()` - 全タイマーの即時実行
6. `runOnlyPendingTimers()` - 保留中タイマーのみ実行
7. `clearAllTimers()` - 全タイマーのクリア
8. `setSystemTime()` - システム時刻の設定
9. `now()` - 現在時刻の取得

**関連システム・外部連携**：`@sinonjs/fake-timers`ライブラリをModern実装の内部で使用。Legacy実装は独自実装。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | Timer Mocksページ | 参照画面 | フェイクタイマーの使い方説明 |
| 20 | Jestオブジェクトページ | 参照画面 | jest.useFakeTimers()のAPI説明 |

## 機能種別

タイマーモック / 時間制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config | FakeTimersConfig | No | フェイクタイマー設定 | オブジェクト |
| config.advanceTimers | boolean\|number | No | 自動時間進行の設定 | boolean or number |
| config.doNotFake | Array<FakeableAPI> | No | フェイク対象外のAPI | 配列 |
| config.now | number\|Date | No | 初期時刻 | 数値またはDate |
| config.timerLimit | number | No | タイマー実行上限 | 正の整数 |
| msToRun | number | Yes (advanceTimersByTime) | 進行ミリ秒数 | 正の整数 |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| now() | number | 現在のフェイク時刻 |
| getRealSystemTime() | number | 実際のシステム時刻 |
| getTimerCount() | number | 保留中のタイマー数 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. useFakeTimers()呼び出し
   └─ グローバルタイマーAPIをフェイク実装に置換
2. テストコード内でsetTimeout等を使用
   └─ フェイク実装がタイマーを内部キューに登録
3. advanceTimersByTime(ms)呼び出し
   └─ 指定時間内のタイマーを順次実行
4. useRealTimers()呼び出し
   └─ オリジナルのタイマーAPIを復元
```

### フローチャート

```mermaid
flowchart TD
    A[useFakeTimers] --> B[グローバルAPI置換]
    B --> C[setTimeout/setInterval等をフェイク化]
    C --> D[テストコード実行]
    D --> E{タイマー進行方法}
    E -->|advanceTimersByTime| F[指定時間分進行]
    E -->|runAllTimers| G[全タイマー実行]
    E -->|advanceTimersToNextTimer| H[次タイマーまで進行]
    F --> I[対象タイマーのコールバック実行]
    G --> I
    H --> I
    I --> J[useRealTimers]
    J --> K[オリジナルAPI復元]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | タイマー上限 | デフォルト100,000回でループ検出 | runAllTimers/advanceTimersByTime |
| BR-02 | タイマー順序 | expiry順に実行 | 複数タイマー存在時 |
| BR-03 | 再帰タイマー | setIntervalは再スケジュール | interval実行後 |
| BR-04 | 警告出力 | フェイク未有効時に操作すると警告 | advanceTimers系呼び出し時 |

### 計算ロジック

タイマー実行判定: timer.expiry <= currentTime + msToRun

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | Error | タイマー上限超過 | timerLimitを増やすか、無限ループを修正 |
| N/A | Warning | フェイク未有効時のadvance | useFakeTimers()を先に呼び出す |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

タイマーキューは効率的にソートされ、次タイマー取得はO(n)で実行。

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

特になし

## 備考

- Modern実装（デフォルト）: @sinonjs/fake-timersを使用
- Legacy実装: 独自のタイマー管理ロジック
- 設定で切り替え可能（legacyFakeTimers: true）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/jest-fake-timers/src/index.ts` | エクスポート構造 |

**主要処理フロー**:
- **8行目**: LegacyFakeTimersのエクスポート
- **9行目**: ModernFakeTimers（デフォルト）のエクスポート

#### Step 2: Modern実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | modernFakeTimers.ts | `packages/jest-fake-timers/src/modernFakeTimers.ts` | @sinonjs/fake-timers統合 |

**主要処理フロー**:
- **8-14行目**: @sinonjs/fake-timersからのインポート
- **18-37行目**: FakeTimersクラスのコンストラクタ
- **39-43行目**: clearAllTimers - clock.reset()呼び出し
- **49-53行目**: runAllTimers - clock.runAll()呼び出し
- **73-85行目**: advanceTimersToNextTimer - clock.next()とtick(0)
- **101-105行目**: advanceTimersByTime - clock.tick(msToRun)
- **133-143行目**: useFakeTimers - fakeTimers.install()
- **153-157行目**: setSystemTime - clock.setSystemTime()
- **205-235行目**: _toSinonFakeTimersConfig - 設定変換

**読解のコツ**: @sinonjs/fake-timersのInstalledClock型が内部時計の役割を果たす。

#### Step 3: Legacy実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | legacyFakeTimers.ts | `packages/jest-fake-timers/src/legacyFakeTimers.ts` | 独自タイマー管理 |

**主要処理フロー**:
- **29-35行目**: Timer型定義（type, callback, expiry, interval）
- **67-120行目**: コンストラクタ、オリジナルAPIの保存
- **122-125行目**: clearAllTimers
- **204-242行目**: runAllTimers - タイマーループ処理
- **260-275行目**: advanceTimersToNextTimer
- **277-310行目**: advanceTimersByTime
- **345-376行目**: useRealTimers - オリジナルAPI復元
- **378-410行目**: useFakeTimers - フェイクAPIインストール
- **562-584行目**: _fakeSetTimeout - タイマー登録処理
- **586-602行目**: _getNextTimerHandleAndExpiry - 次タイマー取得

#### Step 4: 共通パターンを理解する

両実装に共通するメソッドシグネチャを確認する。

| メソッド | Modern | Legacy |
|---------|--------|--------|
| useFakeTimers | clock.install() | setGlobal置換 |
| useRealTimers | clock.uninstall() | setGlobal復元 |
| advanceTimersByTime | clock.tick() | _timers走査 |
| runAllTimers | clock.runAll() | ループ処理 |

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

```
ModernFakeTimers.useFakeTimers() (modernFakeTimers.ts:133)
    │
    ├─ _toSinonFakeTimersConfig() (modernFakeTimers.ts:205)
    │
    └─ fakeTimers.install() (@sinonjs/fake-timers)
           │
           └─ clock オブジェクト生成

ModernFakeTimers.advanceTimersByTime(ms) (modernFakeTimers.ts:101)
    │
    ├─ _checkFakeTimers() (modernFakeTimers.ts:187)
    │
    └─ clock.tick(ms)

LegacyFakeTimers.useFakeTimers() (legacyFakeTimers.ts:378)
    │
    ├─ _createMocks() (legacyFakeTimers.ts:441)
    │      └─ moduleMocker.fn() でフェイク関数生成
    │
    └─ setGlobal() でグローバル置換

LegacyFakeTimers.advanceTimersByTime(ms) (legacyFakeTimers.ts:277)
    │
    ├─ _checkFakeTimers() (legacyFakeTimers.ts:418)
    │
    └─ _getNextTimerHandleAndExpiry() (legacyFakeTimers.ts:586)
           │
           └─ _runTimerHandle() (legacyFakeTimers.ts:604)
```

### データフロー図

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

useFakeTimers() ────────▶ グローバルAPI置換 ──────────▶ フェイクタイマー環境

setTimeout(cb, delay) ──▶ _fakeSetTimeout ─────────────▶ タイマー登録
                              │
                              └── _timers Map に追加

advanceTimersByTime(ms) ▶ タイマー走査 ─────────────────▶ コールバック実行
                              │
                              ├── expiry <= now + ms ?
                              │      └── callback()
                              │
                              └── now += ms
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-fake-timers/src/index.ts` | ソース | エクスポート定義 |
| modernFakeTimers.ts | `packages/jest-fake-timers/src/modernFakeTimers.ts` | ソース | Modern実装（@sinonjs/fake-timers使用） |
| legacyFakeTimers.ts | `packages/jest-fake-timers/src/legacyFakeTimers.ts` | ソース | Legacy実装（独自タイマー管理） |
