# 機能設計書 93-unittest（単体テストフレームワーク）

## 概要

本ドキュメントは、Nim標準ライブラリの`unittest`モジュールの機能設計を記述する。このモジュールは、Nimプログラムの単体テストを記述・実行するためのフレームワークを提供する。

### 本機能の処理概要

unittestモジュールは、テストスイートとテストケースの定義、アサーション、テスト結果の出力フォーマット（コンソール、JUnit XML）などの機能を提供する。テストの実行状態と結果を追跡し、成功・失敗・スキップのステータスを報告する。

**業務上の目的・背景**：ソフトウェア品質を確保するためには、自動化された単体テストが不可欠である。unittestモジュールは、Nimプロジェクトにおけるテスト駆動開発（TDD）やCI/CDパイプラインでのテスト自動化を支援し、コードの信頼性向上に貢献する。

**機能の利用シーン**：
- 関数やプロシージャの振る舞いを検証するテストの記述
- コンパイル＆実行でテスト結果を確認（`nim c -r test.nim`）
- CI/CDパイプラインでのJUnit XML形式でのテスト結果出力
- 特定のテストのみを実行するフィルタリング
- setup/teardownによる共通の前処理・後処理の定義

**主要な処理内容**：
1. `suite`マクロ: テストスイート（テストグループ）の定義
2. `test`マクロ: 個別テストケースの定義
3. `check`マクロ: 条件検証（失敗時にチェックポイント情報を出力）
4. `require`テンプレート: 失敗時に即座にテストを中断する検証
5. `expect`マクロ: 例外発生の検証
6. `skip`テンプレート: テストのスキップ
7. OutputFormatter: テスト結果の出力フォーマット（Console、JUnit）

**関連システム・外部連携**：
- Testament: Nimの公式テストランナー（より高度なテスト分離が必要な場合に推奨）
- CI/CDシステム: JUnit XML出力によるテスト結果連携

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面関連なし（ライブラリモジュール） |

## 機能種別

テストフレームワーク / ユーティリティライブラリ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | suiteまたはtestの名前 | 空でない文字列 |
| body | untyped | Yes | テスト本体のコード | 有効なNimコード |
| conditions | untyped | Yes | checkマクロの検証条件 | ブール式 |
| exceptions | varargs[typed] | Yes | expectマクロで期待する例外型 | Exception継承型 |

### 入力データソース

- テストコードの直接記述
- コマンドライン引数（テストフィルタ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| TestResult | object | テスト名、スイート名、ステータス（OK/FAILED/SKIPPED） |
| 終了コード | int | 成功時0、失敗時1 |
| JUnit XML | Stream | JUnit形式のテストレポート（JUnitOutputFormatter使用時） |

### 出力先

- 標準出力（コンソール）
- ファイル（JUnit XML形式）

## 処理フロー

### 処理シーケンス

```
1. テストファイルのコンパイル・実行
   └─ ensureInitialized()でフォーマッタとフィルタを初期化

2. suiteマクロ展開時
   ├─ suiteStartedをフォーマッタに通知
   ├─ setup/teardownテンプレートを定義
   └─ body内のtestマクロを順次実行

3. testマクロ展開時
   ├─ shouldRun()でフィルタ判定
   ├─ testStartedをフォーマッタに通知
   ├─ setup実行（定義されている場合）
   ├─ テスト本体実行
   │   └─ 例外発生時はcheckpoint追加してfail()
   ├─ teardown実行（defer経由）
   └─ testEndedをフォーマッタに通知

4. checkマクロ展開時
   ├─ 条件式を評価
   ├─ 失敗時: checkpoint追加 + fail()呼び出し
   └─ 成功時: 何もしない

5. 全テスト完了後
   └─ 終了コードを設定（失敗テストあり: 1、なし: 0）
```

### フローチャート

```mermaid
flowchart TD
    A[テスト実行開始] --> B[ensureInitialized]
    B --> C{コマンドライン引数?}
    C -->|Yes| D[testsFiltersに追加]
    C -->|No| E[フィルタなし]
    D --> F[suite処理]
    E --> F

    F --> G[suiteStarted通知]
    G --> H[setup/teardown定義]
    H --> I[test処理ループ]

    I --> J{shouldRun?}
    J -->|No| I
    J -->|Yes| K[testStarted通知]
    K --> L[setup実行]
    L --> M[テスト本体]
    M --> N{例外発生?}
    N -->|Yes| O[checkpoint + fail]
    N -->|No| P[teardown実行]
    O --> P
    P --> Q[testEnded通知]
    Q --> I

    I --> R[suiteEnded通知]
    R --> S{失敗テストあり?}
    S -->|Yes| T[終了コード = 1]
    S -->|No| U[終了コード = 0]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ネストテスト | テストはネスト可能だが、ネストテストの失敗は親テストを失敗させない | ネストされたテスト |
| BR-02 | setup継承 | setupは上位スイートから継承され、ローカルでオーバーライド可能 | suiteのネスト |
| BR-03 | グロブフィルタ | テスト名に単一の`*`を使ったグロブパターンマッチが可能 | コマンドライン指定時 |
| BR-04 | スイート区切り | スイート名とテスト名は`::`で区切って指定 | コマンドラインフィルタ |
| BR-05 | 即時中断 | abortOnError=trueの場合、失敗時に即座にquit(1) | abortOnError設定時 |

### 計算ロジック

特になし（テストフレームワークのため）

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

該当なし（本モジュールはデータベース操作を行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FAILED | TestStatus | check/requireが失敗 | テストコードまたは実装を修正 |
| 終了コード1 | プロセス | テスト失敗が1つ以上 | CI/CDで検知してビルド失敗とする |
| Unhandled exception | 例外 | テスト内で未処理の例外 | checkpoint出力後にFAILED |

### リトライ仕様

該当なし（テスト失敗時のリトライは推奨されない）

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

該当なし（データベース操作を行わない）

## パフォーマンス要件

- 個々のテストの実行時間は計測されJUnit XML出力に含まれる
- テスト全体の実行時間に制限はないが、CI/CDのタイムアウト設定に注意

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

- テストコードは本番環境では実行しないこと
- 機密情報をテスト出力に含めないよう注意

## 備考

- Testamentツールはより高度なプロセス分離を提供するため、重要なテストにはTestamentの使用を推奨
- `-d:nimUnittestOutputLevel`でPRINT_ALL/PRINT_FAILURES/PRINT_NONEを選択可能
- `-d:nimUnittestColor`でauto/on/offを選択可能
- `-d:nimUnittestAbortOnError`で失敗時即時終了を設定可能

---

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

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

### 推奨読解順序

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

まず、テスト結果やフォーマッタの型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | unittest.nim | `lib/pure/unittest.nim` | 124-142行目: TestStatus、OutputLevel、TestResult型の定義 |
| 1-2 | unittest.nim | `lib/pure/unittest.nim` | 143-168行目: OutputFormatter、ConsoleOutputFormatter、JUnitOutputFormatterの定義 |
| 1-3 | unittest.nim | `lib/pure/unittest.nim` | 170-181行目: グローバル変数（abortOnError、checkpoints、formatters等） |

**読解のコツ**: TestStatusは3つの状態（OK、FAILED、SKIPPED）を持つ列挙型。OutputFormatterはオブジェクト指向的なポリモーフィズムを使用している。

#### Step 2: フォーマッタの仕組みを理解する

テスト結果の出力方法を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | unittest.nim | `lib/pure/unittest.nim` | 199-211行目: OutputFormatterの基底メソッド定義 |
| 2-2 | unittest.nim | `lib/pure/unittest.nim` | 262-304行目: ConsoleOutputFormatterのメソッド実装 |
| 2-3 | unittest.nim | `lib/pure/unittest.nim` | 321-399行目: JUnitOutputFormatterのメソッド実装 |

**主要処理フロー**:
- **262-269行目**: suiteStartedでスイート名を表示（色付きオプション）
- **282-301行目**: testEndedでテスト結果を表示（OK=緑、FAILED=赤、SKIPPED=黄）

#### Step 3: suiteマクロを理解する

テストスイートの定義方法を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | unittest.nim | `lib/pure/unittest.nim` | 468-515行目: suiteマクロの実装 |

**主要処理フロー**:
- **499-501行目**: setupテンプレートの定義
- **503-505行目**: teardownテンプレートの定義
- **507行目**: testSuiteNameの設定
- **511-512行目**: suiteStarted通知とbody実行

#### Step 4: testマクロを理解する

個別テストケースの実行フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | unittest.nim | `lib/pure/unittest.nim` | 527-576行目: testマクロの実装 |

**主要処理フロー**:
- **543行目**: shouldRun()でフィルタチェック
- **544行目**: checkpoints初期化
- **545行目**: testStatusIMPLをOKで初期化
- **551行目**: setup実行（定義されている場合）
- **552-553行目**: teardownのdefer登録
- **556-565行目**: 例外ハンドリング

#### Step 5: checkマクロを理解する

条件検証の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | unittest.nim | `lib/pure/unittest.nim` | 639-762行目: checkマクロの実装 |

**主要処理フロー**:
- **660-710行目**: inspectArgsで引数を解析し、失敗時に値を表示するためのコード生成
- **712-745行目**: 呼び出し式の場合の処理
- **753-762行目**: その他の式の場合の処理

#### Step 6: テストフィルタリングを理解する

コマンドラインからのテスト選択を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | unittest.nim | `lib/pure/unittest.nim` | 401-446行目: glob、matchFilter、shouldRun関数 |
| 6-2 | unittest.nim | `lib/pure/unittest.nim` | 448-456行目: ensureInitialized関数 |

**主要処理フロー**:
- **401-418行目**: globで`*`を使ったパターンマッチ
- **420-434行目**: matchFilterでスイート名::テスト名のマッチング
- **453-456行目**: コマンドライン引数からフィルタを読み込み

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

```
テスト実行開始
    │
    ├─ ensureInitialized()
    │      ├─ defaultConsoleFormatter()作成
    │      └─ paramStr()からtestsFiltersに追加
    │
    └─ suite展開
           │
           ├─ suiteStarted(formatter, name)
           │
           ├─ test展開（繰り返し）
           │      │
           │      ├─ shouldRun(suiteName, testName)
           │      │      └─ matchFilter() → glob()
           │      │
           │      ├─ testStarted(formatter, name)
           │      │
           │      ├─ testSetupIMPL()（定義時）
           │      │
           │      ├─ body実行
           │      │      └─ check展開
           │      │             ├─ inspectArgs()
           │      │             └─ checkpoint() + fail()（失敗時）
           │      │
           │      ├─ testTeardownIMPL()（defer）
           │      │
           │      └─ testEnded(formatter, TestResult)
           │
           └─ suiteEnded(formatter)
```

### データフロー図

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

コマンドライン引数 ─────▶ paramStr()で読み取り ────────▶
                              │
                              ▼
                         testsFilters（HashSet）
                              │
                              ▼
                         shouldRun()でフィルタ
                              │
                              ▼
テストコード ──────────▶ test/check/require実行 ───────▶
                              │
                              ▼
                         TestResult生成
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
           ConsoleOutputFormatter  JUnitOutputFormatter
                    │                   │
                    ▼                   ▼
              標準出力              XMLファイル
                    │
                    ▼
              終了コード ────────────────────────────▶ 0 or 1
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| unittest.nim | `lib/pure/unittest.nim` | ソース | 単体テストフレームワークのメイン実装 |
| macros.nim | `lib/core/macros.nim` | 依存元 | マクロ定義用ユーティリティ |
| strutils.nim | `lib/pure/strutils.nim` | 依存元 | 文字列操作 |
| streams.nim | `lib/pure/streams.nim` | 依存元 | JUnit出力用ストリーム |
| times.nim | `lib/pure/times.nim` | 依存元 | テスト時間計測 |
| sets.nim | `lib/pure/collections/sets.nim` | 依存元 | testsFilters用HashSet |
| terminal.nim | `lib/pure/terminal.nim` | 依存元 | 色付きコンソール出力 |
| exitprocs.nim | `lib/std/exitprocs.nim` | 依存元 | 終了コード設定 |
