BluePeriod Docs
開発

テスト戦略と導入方針

テスト戦略と導入方針

1. 現状

BluePeriodは現在ゼロテスト環境です。自動テストが存在せず、開発者の手動テストに依存しています。

リスク:

  • リグレッション(既存機能の意図しない破壊)を防げない
  • リファクタリングが困難
  • CI/CDでの品質チェックが不可能

2. テスト導入の基本方針

2.1. 段階的導入

一度に全てをテストするのではなく、以下の順序で導入します:

  1. フェーズ1: Service層のユニットテスト(最優先)
  2. フェーズ2: ツールの統合テスト
  3. フェーズ3: コンポーネントのテスト(将来)

2.2. テストフレームワーク

Bun Testを採用します。

理由:

  • Bunに既に移行済み
  • Jest互換のAPIで学習コストが低い
  • 高速(実行時間が短い)
  • 組み込みのモック機能
  • TypeScriptサポートが優れている

3. テストの種類と役割

┌─────────────────────────────────────────────────────────────┐
│                     テストピラミッド                          │
├─────────────────────────────────────────────────────────────┤
│  E2Eテスト (少)                                              │
│   ┌─────────┐                                               │
│  │ 統合テスト  │  Agent + Tool + Service の結合テスト          │
│   └─────────┘                                               │
│    ┌───────┐                                                │
│   │ユニットテスト│  Service, Tool 個別のテスト(多数)         │
│    └───────┘                                                │
└─────────────────────────────────────────────────────────────┘

3.1. ユニットテスト

対象: Service層、純粋関数 目的: 個々の関数が正しく動作することを保証

// 例: LocalProjectService.getProjectMetadata()
describe("LocalProjectService.getProjectMetadata", () => {
  it("should return project metadata", async () => {
    const result = await LocalProjectService.getProjectMetadata("test-id");
    expect(result).toEqual({
      id: "test-id",
      title: "Test Project",
      author: "Test Author",
      // ...
    });
  });
});

3.2. 統合テスト

対象: ツール、エージェント 目的: Service、Tool、Agentが正しく連携することを保証

// 例: blueperiod_get_project tool
describe("blueperiod_get_project tool", () => {
  it("should retrieve lightweight project data", async () => {
    const tool = createProjectTools(mockGet, mockSet)[1]; // get_project
    const result = await tool.func({ projectId: "test-id" });
    expect(result).toContain("Test Project");
  });
});

3.3. E2Eテスト

対象: ユーザー操作的なシナリオ 目的: システム全体が正しく動作することを保証 : 導入の後半で検討


4. ディレクトリ構造

next-app/src/
├── lib/
│   └── features/
│       ├── projects/
│       │   ├── local-project-service.ts
│       │   └── __tests__/          # Serviceのテスト
│       │       ├── local-project-service.test.ts
│       │       └── tools.test.ts
│       ├── editorial/
│       │   ├── tools/
│       │   │   ├── plot-tools.ts
│       │   │   └── __tests__/      # Toolのテスト
│       │   │       ├── plot-tools.test.ts
│       │   │       └── manuscript-tools.test.ts
│       └── characters/
│           ├── tools.ts
│           └── __tests__/
│               └── tools.test.ts
├── ai/
│   └── agents/
│       └── __tests__/              # Agentのテスト
│           └── assistantGraph.test.ts

5. モック戦略

5.1. IndexedDBのモック

db (Dexie.js) をモックする必要があります:

// __mocks__/db.ts
export const db = {
  projects: {
    get: mock(() => Promise.resolve({})),
    toArray: mock(() => Promise.resolve([])),
    // ...
  }
};

5.2. Jotai Atomのモック

const mockGet = mock((atom) => mockValue);
const mockSet = mock();

6. 最初のテスト作成タスク

タスク1: テスト環境のセットアップ

  1. package.json にテストスクリプトを追加
  2. モック用ファイルを作成
  3. CI設定にテスト実行を追加

タスク2: Service層のテスト(優先度高)

以下のServiceから順にテストを追加:

  1. LocalProjectService

    • getProjectMetadata()
    • getLightweightProject()
    • listProjects()
  2. ManuscriptService

    • getContent()
    • updateContent()
  3. PlotService

    • getStructuredPlot()
    • addPlotItem()

タスク3: ツールのテスト

  1. createProjectTools() の各ツール
  2. createPlotTools() の各ツール
  3. createManuscriptTools() の各ツール

7. 開発プロセスへの組み込み

7.1. 新規機能開発時

1. まずテストを書く(TDD推奨だが、必須ではない)
2. 実装する
3. テストをパスすることを確認
4. PR時にテスト結果を表示

7.2. バグ修正時

1. バグを再現するテストを書く
2. バグを修正する
3. テストがパスすることを確認
4. 同様のバグが発生しないことを保証

7.3. CI/CDでの実行

# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - run: bun test

8. カバレッジ目標

フェーズカバレッジ目標期限
フェーズ1完了時Service層: 60%1ヶ月
フェーズ2完了時Service + Tools: 50%2ヶ月
フェーズ3完了時全体: 40%将来検討

: 100%を目指すのではなく、重要なパスを確実にカバーすることを優先。


9. 最初のテスト実装例

9.1. LocalProjectService.getProjectMetadata()

// next-app/src/lib/features/projects/__tests__/local-project-service.test.ts
import { describe, it, expect, mock } from "bun:test";
import { db } from "@/lib/db";

// モックのセットアップ
mock.module "@/lib/db", () => ({
  db: {
    projects: {
      get: (id: string) => Promise.resolve({
        id,
        title: "Test Project",
        author: "Test Author",
        createdAt: new Date("2024-01-01"),
        updatedAt: new Date("2024-01-01")
      })
    }
  }
});

describe("LocalProjectService", () => {
  describe("getProjectMetadata", () => {
    it("should return project metadata without content", async () => {
      const { LocalProjectService } = await import("../local-project-service");

      const result = await LocalProjectService.getProjectMetadata("test-id");

      expect(result).toEqual({
        id: "test-id",
        title: "Test Project",
        author: "Test Author",
        createdAt: new Date("2024-01-01"),
        updatedAt: new Date("2024-01-01")
      });
    });

    it("should return null for non-existent project", async () => {
      // モックをnullを返すように上書き
      const result = await LocalProjectService.getProjectMetadata("non-existent");

      expect(result).toBeNull();
    });
  });
});

9.2. Toolのテスト例

// next-app/src/lib/features/projects/__tests__/tools.test.ts
import { describe, it, expect } from "bun:test";

describe("createProjectTools", () => {
  it("should create tools with blueperiod_ prefix", () => {
    const mockGet = () => ({});
    const mockSet = () => {};
    const { createProjectTools } = await import("../tools");

    const tools = createProjectTools(mockGet, mockSet);

    expect(tools[0].name).toBe("blueperiod_list_projects");
    expect(tools[1].name).toBe("blueperiod_get_project");
    // ...
  });
});

10. 開発体制の定義

10.1. 役割と責任

役割責任
開発者実装と同時にテストを作成
レビューアーテストが十分かどうかをレビュー
CI自動的にテストを実行

10.2. テスト未追加の場合の対応

  • レビュアーはテストがない場合、PRを承認しない
  • ただし、調査・実験的コードの場合は例外とする

11. まとめ

  1. Bun Testを採用
  2. Service層のユニットテストから開始
  3. モックを活用して外部依存を切り離す
  4. 段階的に導入し、カバレッジを徐々に上げる
  5. CI/CDに組み込んで自動実行

次のアクション:

  • package.jsonにテストスクリプト追加
  • LocalProjectServiceの最初のテストを作成
  • CI設定を追加

On this page