mcpField
V
vitest

vitest

github.com/sablier-labsテスト・品質940 回インストール5 週間前に更新
リポジトリ

vitest について

vitest は、TypeScript の React/Next.js プロジェクトで Vitest v4 を使うためのテストスキルです。テスト作成、失敗の調査、テストスイートの保守を支援し、構成、モック、非同期テスト、カバレッジ、デバッグを扱います。

主な機能

  1. 01ユニットテストと非同期テストの作成
  2. 02関数、モジュール、タイマーのモック
  3. 03テスト構成と状態のリセット
  4. 04失敗の調査とカバレッジ確認
  5. 05React と monorepo テストの参考資料

活用シーン

  1. 01TypeScript プロジェクトへのユニットテスト追加
  2. 02React/Next.js コードと DOM 動作のテスト
  3. 03モック不具合やテスト状態の漏れの調査
  4. 04カバレッジ確認と失敗ケースのデバッグ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/sablier-labs/agent-skills --skill vitest

インストール後、Claude Code で/vitestを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
monorepo-testing.md
testing-patterns.md
troubleshooting.md

詳細

あなたの役割

あなたは、TypeScript の React/Next.js プロジェクトにおける Vitest v4 のテスト作成に精通しています。ユーザーが高品質なテストを作成し、失敗をデバッグし、テストスイートを効率よく保守できるよう支援します。

一般的なセットアップ:

  • jsdom 環境での Vitest v4
  • グローバルの有効化(describe、test、expect、vi)
  • プロジェクトごとに設定されたパスエイリアス

クイックスタート

テストの実行

# Run all unit tests
nlx vitest run

# Run tests matching pattern
nlx vitest run tokens

# Run specific test file
nlx vitest run src/utils/format.test.ts

# Run tests with matching name
nlx vitest run -t "adds token"

# Watch mode
nlx vitest

最初のテストを書く

ファイル名: *.test.ts または *.test.tsx

配置場所: ソースファイルと同じ場所に配置

import { describe, test, expect } from "vitest";
import { myFunction } from "./my-function";

describe("myFunction", () => {
  test("returns expected value", () => {
    expect(myFunction(5)).toBe(10);
  });
});

プロジェクト固有のパターン

テストの構成

視覚的な区切りと分かりやすいブロックを使用します。

describe("TokenStore", () => {
  /* ----------------------------------------------------------------
   * Setup
   * ------------------------------------------------------------- */

  const validToken = { address: "0x123", symbol: "TEST" };

  afterEach(() => {
    // Reset state between tests
    useTokensStore.getState().clearAll();
  });

  /* ----------------------------------------------------------------
   * Adding tokens
   * ------------------------------------------------------------- */

  describe("addToken", () => {
    test("adds valid token and returns true", () => {
      const success = useTokensStore.getState().addToken(validToken);
      expect(success).toBe(true);
    });
  });
});

クリーンアップのパターン

afterEach() で必ず状態をリセットします。

import { afterEach } from "vitest";

afterEach(() => {
  // Reset mocks
  vi.clearAllMocks();

  // Reset environment
  process.env.NODE_ENV = originalEnv;

  // Reset stores
});

ファクトリーモックのパターン

複雑なモックにはファクトリー関数を使用します。

// __mocks__/localStorage.ts
import { vi } from "vitest";

export function createLocalStorageMock() {
  const store = new Map<string, string>();

  return {
    getItem: vi.fn((key: string) => store.get(key) ?? null),
    setItem: vi.fn((key: string, value: string) => {
      store.set(key, value);
    }),
    removeItem: vi.fn((key: string) => {
      store.delete(key);
    }),
    clear: vi.fn(() => {
      store.clear();
    })
  };
}

// Usage in tests
import { createLocalStorageMock } from "./__mocks__/localStorage";

const mockStorage = createLocalStorageMock();
global.localStorage = mockStorage as Storage;

共有セットアップファイル

グローバルモックと設定はセットアップファイル(例:tests/setup.ts)に記述します。

import { vi } from "vitest";

// Mock logger for all tests
vi.mock("@/utils/logger", () => ({
  createLogger: vi.fn(() => ({
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn()
  }))
}));

よくあるテストシナリオ

ユーティリティのテスト

import { describe, test, expect, afterEach } from "vitest";
import { getEnvironment } from "./environment";

describe("getEnvironment", () => {
  const originalEnv = process.env.NODE_ENV;

  afterEach(() => {
    process.env.NODE_ENV = originalEnv;
  });

  test("returns production when NODE_ENV is production", () => {
    process.env.NODE_ENV = "production";
    expect(getEnvironment()).toBe("production");
  });

  test("returns development by default", () => {
    process.env.NODE_ENV = undefined;
    expect(getEnvironment()).toBe("development");
  });
});

非同期テスト

test("async function resolves correctly", async () => {
  const result = await fetchData();
  expect(result).toEqual({ data: "value" });
});

test("async function rejects with error", async () => {
  await expect(failingFunction()).rejects.toThrow("Error message");
});

関数のモック

import { vi } from "vitest";

// Mock a function
const mockCallback = vi.fn((x: number) => x * 2);
mockCallback(5);
expect(mockCallback).toHaveBeenCalledWith(5);
expect(mockCallback).toHaveReturnedWith(10);

// Spy on object method
const spy = vi.spyOn(console, "log").mockImplementation(() => {});
console.log("test");
expect(spy).toHaveBeenCalledWith("test");
spy.mockRestore();

モジュールのモック

// At top level, before imports
vi.mock("./api-client", () => ({
  fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: "Test" }))
}));

import { fetchUser } from "./api-client";

test("uses mocked API", async () => {
  const user = await fetchUser();
  expect(user.name).toBe("Test");
});

タイマーのモック

import { vi } from "vitest";

test("debounced function", () => {
  vi.useFakeTimers();

  const callback = vi.fn();
  const debounced = debounce(callback, 1000);

  debounced();
  debounced();
  debounced();

  vi.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalledTimes(1);

  vi.useRealTimers();
});

テスト失敗のデバッグ

テスト出力の読み方

次の情報に注目します。

  • ファイルと行番号 - 失敗が発生した場所
  • 期待値と受信値 - 何が問題だったか
  • スタックトレース - フレームワーク内部の情報は無視し、自分のコードに注目

よくある失敗

テスト間で状態が漏れる:

// Problem: Previous test left state
test("first test", () => {
  store.addItem("test");
});

test("second test", () => {
  expect(store.items).toHaveLength(0); // Fails! Still has "test"
});

// Solution: Add cleanup
afterEach(() => {
  store.clear();
});

モックが動作しない:

// Problem: Mock path doesn't match import
vi.mock("./utils/logger");
import { logger } from "@/utils/logger"; // Different path!

// Solution: Match exact import path
vi.mock("@/utils/logger");

非同期処理がタイムアウトする:

// Problem: Default 5s timeout too short
test("slow operation", async () => {
  await verySlowOperation(); // Times out
});

// Solution: Increase timeout
test("slow operation", async () => {
  await verySlowOperation();
}, 10000); // 10 second timeout

デバッグツール

nlx vitest --reporter=verbose   # Detailed output
nlx vitest --ui                  # Visual debugging interface
nlx vitest --coverage            # See what's tested
nlx vitest --inspect             # Node debugger
nlx vitest --run                 # Disable watch mode

ベストプラクティス

DO

  • テストをソースファイルと同じ場所に配置する(feature.ts + feature.test.ts)
  • 関連するテストをまとめるために describe ブロックを使用する
  • 状態/モックをクリーンアップする afterEach() を追加する
  • 分かりやすさのために視覚的な区切りを使用する(/* --- */)
  • 実装ではなく振る舞いをテストする
  • モックには明示的な型注釈を使用する
  • テストの対象を絞り、独立性を保つ
  • バグを修正する前にテストを書く(まずバグを再現する)

DON'T

  • 実装の詳細(内部変数)をテストしない
  • テスト間で状態を共有しない
  • すべてをモックしない(境界のみをモックする:ネットワーク、ストレージ、時間)
  • モック/タイマーの復元を忘れない
  • テストで any 型を使用しない
  • DOM 構造に依存する壊れやすいテストを作成しない
  • テストユーティリティに後方互換性のためのハックを追加しない

発展的なトピック

詳しくは ./references/ ディレクトリを参照してください。

  • testing-patterns.md - パターンの総合ガイド(コンポーネントテスト、複雑なモック、非同期パターン)
  • monorepo-testing.md - ワークスペース固有の方針(共有テストとアプリテスト、パスエイリアス、構成)
  • troubleshooting.md - デバッグガイド(よくあるエラー、パフォーマンス、カバレッジ、CI/CD)

カバレッジ分析

カバレッジを追加するには:

// vitest.config.ts
export default defineConfig({
  test: {
    coverage: {
      provider: "v8",
      reporter: ["text", "html", "json"],
      exclude: ["**/*.test.ts", "**/__mocks__/**", "**/node_modules/**"]
    }
  }
});

次のコマンドで実行します:nlx vitest --coverage

設定リファレンス

設定例:vitest.config.ts

{
  environment: "jsdom",           // React/DOM APIs available
  globals: true,                  // No imports needed for describe/test/expect
  include: ["**/*.test.{js,ts,tsx}"],
  exclude: ["**/node_modules/**", "**/e2e/**"],
  setupFiles: ["./tests/setup.ts"],
  alias: {
    "@": "./src",
    // Add your project's path aliases
  },
}

次のステップ

  1. コンポーネントテストについて - ./references/testing-patterns.md(React Testing Library のセットアップ)を参照
  2. モノレポ固有の方針について - ./references/monorepo-testing.md を参照
  3. デバッグのヘルプについて - ./references/troubleshooting.md を参照

シンプルなユニットテストから始め、必要に応じてコンポーネントテストを追加します。

コメント