vitest の使い方

Next generation testing framework powered by Vite

v4.1.2/週MITテスト
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

Vitest の使い方完全ガイド — Vite時代の次世代テストフレームワーク

一言でいうと

VitestはViteのエコシステム上に構築された高速なJavaScript/TypeScriptテストフレームワークです。Jestとほぼ互換のAPIを持ちながら、Viteのトランスフォームパイプラインとホットモジュールリロード(HMR)を活用することで、圧倒的な実行速度を実現します。

どんな時に使う?

  1. Viteベースのプロジェクトにテストを導入したい時vite.config.ts の設定(エイリアス、プラグインなど)をそのままテストでも共有できるため、設定の二重管理が不要になります。
  2. Jestの遅さに不満がある時 — ESM対応やTypeScriptのトランスパイルでJestの設定に苦労している場合、Vitestに移行するだけで設定がシンプルになり、実行速度も大幅に改善します。
  3. コンポーネントテスト・ユニットテストを高速に回したい時 — ウォッチモードがデフォルトで有効になっており、変更されたファイルに関連するテストだけを再実行するため、開発中のフィードバックループが極めて短くなります。

インストール

# npm
npm install -D vitest

# yarn
yarn add -D vitest

# pnpm
pnpm add -D vitest

Note: この記事はVitest v4.x系を前提としています。バージョンによってAPIや挙動が異なる場合があります。公式ドキュメント(https://vitest.dev/)で最新情報を確認してください。

基本的な使い方

最小構成

package.json にスクリプトを追加します。

{
  "scripts": {
    "test": "vitest",
    "test:run": "vitest run"
  }
}

vitest コマンドはデフォルトでウォッチモードで起動します。CI環境などで一度だけ実行したい場合は vitest run を使います。

設定ファイル

vite.config.ts にテスト設定を追加するか、専用の vitest.config.ts を作成します。

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,          // describe, it, expect をimportなしで使用
    environment: 'node',    // 'jsdom' | 'happy-dom' | 'node'
    include: ['src/**/*.{test,spec}.{ts,tsx}'],
    coverage: {
      provider: 'v8',       // 'v8' | 'istanbul'
      reporter: ['text', 'json', 'html'],
    },
  },
});

はじめてのテスト

// src/utils/math.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function divide(a: number, b: number): number {
  if (b === 0) throw new Error('Division by zero');
  return a / b;
}
// src/utils/math.test.ts
import { describe, it, expect } from 'vitest';
import { add, divide } from './math';

describe('math utils', () => {
  it('should add two numbers', () => {
    expect(add(1, 2)).toBe(3);
  });

  it('should divide two numbers', () => {
    expect(divide(10, 2)).toBe(5);
  });

  it('should throw on division by zero', () => {
    expect(() => divide(10, 0)).toThrowError('Division by zero');
  });
});
npx vitest run

よく使うAPI — Vitestの使い方を深掘り

1. vi.fn() — モック関数の作成

Jestの jest.fn() に相当します。関数の呼び出し回数や引数を追跡できます。

import { describe, it, expect, vi } from 'vitest';

describe('vi.fn()', () => {
  it('should track calls', () => {
    const mockFn = vi.fn((x: number) => x * 2);

    mockFn(3);
    mockFn(5);

    expect(mockFn).toHaveBeenCalledTimes(2);
    expect(mockFn).toHaveBeenCalledWith(3);
    expect(mockFn.mock.results[0].value).toBe(6);
  });
});

2. vi.mock() — モジュールモック

外部モジュールやプロジェクト内モジュールを丸ごとモックに差し替えます。

import { describe, it, expect, vi } from 'vitest';

// モジュール全体をモック
vi.mock('./api', () => ({
  fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
}));

import { fetchUser } from './api';

describe('vi.mock()', () => {
  it('should return mocked user', async () => {
    const user = await fetchUser(1);
    expect(user).toEqual({ id: 1, name: 'Alice' });
    expect(fetchUser).toHaveBeenCalledWith(1);
  });
});

3. vi.spyOn() — 既存オブジェクトのメソッドを監視

元の実装を保持しつつ、呼び出しを追跡できます。

import { describe, it, expect, vi, afterEach } from 'vitest';

describe('vi.spyOn()', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should spy on console.log', () => {
    const spy = vi.spyOn(console, 'log').mockImplementation(() => {});

    console.log('hello');

    expect(spy).toHaveBeenCalledWith('hello');
  });
});

4. vi.useFakeTimers() / vi.advanceTimersByTime() — タイマーの制御

setTimeoutsetInterval をテスト内で自在に操作できます。

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

function delayedGreeting(callback: () => void) {
  setTimeout(callback, 3000);
}

describe('Fake Timers', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('should call callback after 3 seconds', () => {
    const callback = vi.fn();
    delayedGreeting(callback);

    expect(callback).not.toHaveBeenCalled();

    vi.advanceTimersByTime(3000);

    expect(callback).toHaveBeenCalledOnce();
  });
});

5. test.each() — パラメータ化テスト

同じテストロジックを複数のデータセットで実行します。

import { describe, it, expect } from 'vitest';
import { add } from './math';

describe('add - parameterized', () => {
  it.each([
    [1, 2, 3],
    [0, 0, 0],
    [-1, 1, 0],
    [100, 200, 300],
  ])('add(%i, %i) should return %i', (a, b, expected) => {
    expect(add(a, b)).toBe(expected);
  });
});

類似パッケージとの比較

特徴VitestJestMocha + Chai
実行速度◎ 非常に高速△ 大規模で遅くなりがち○ 普通
TypeScript対応◎ ゼロコンフィグ△ ts-jest等が必要△ 別途設定が必要
ESMサポート◎ ネイティブ△ 実験的○ 対応
Vite設定の共有◎ そのまま使える✕ 不可✕ 不可
Jest互換API◎ ほぼ完全互換✕ 別体系
エコシステム○ 急速に拡大中◎ 最大規模○ 成熟
インブラウザテスト○ 対応✕ jsdomのみ△ Karma等が必要
スナップショットテスト◎ 対応◎ 対応△ プラグインが必要

選定の目安:

  • Viteを使っているなら → Vitest 一択
  • レガシーなWebpackプロジェクトで移行コストを避けたい → Jest
  • 最小限の構成で柔軟に組みたい → Mocha + Chai

注意点・Tips

1. globals: true 使用時は型定義を追加する

globals: true を設定すると describeit をimportなしで使えますが、TypeScriptの型エラーを防ぐために tsconfig.json に型定義を追加してください。

{
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
}

2. Jestからの移行は段階的に

VitestはJestとほぼ互換のAPIを持ちますが、以下の点に注意が必要です。

  • jest.fn()vi.fn() に置換
  • jest.mock()vi.mock() に置換
  • @jest/globals のimportを vitest に変更
  • moduleNameMapper → Viteの resolve.alias に移行

公式のマイグレーションガイドを参照してください。

3. テストの並列実行に注意

Vitestはデフォルトでテストファイルを並列実行します。グローバルな状態(DB、ファイルシステムなど)を共有するテストでは競合が発生する可能性があります。

// 特定のファイルを直列実行にする
// vitest.config.ts
export default defineConfig({
  test: {
    sequence: {
      concurrent: false, // ファイル内のテストを直列実行
    },
    // または特定のファイルだけ pool の設定で制御
  },
});

テスト内で明示的に直列実行を指定することもできます。

describe.sequential('DB tests', () => {
  it('should insert record', async () => { /* ... */ });
  it('should read record', async () => { /* ... */ });
});

4. カバレッジの取得

カバレッジプロバイダーを別途インストールする必要があります。

# v8プロバイダー(推奨・高速)
npm install -D @vitest/coverage-v8

# istanbulプロバイダー
npm install -D @vitest/coverage-istanbul
npx vitest run --coverage

5. UI モードで視覚的にデバッグ

npm install -D @vitest/ui
npx vitest --ui

ブラウザ上でテスト結果をインタラクティブに確認でき、失敗したテストの原因特定が格段に楽になります。

まとめ

VitestはViteの高速なビルドパイプラインを活かした次世代テストフレームワークで、Jest互換のAPIにより学習コストを最小限に抑えながら、ESM・TypeScriptへのネイティブ対応と圧倒的な実行速度を提供します。Viteベースのプロジェクトであれば設定をそのまま共有できるため、導入の手間もほぼありません。新規プロジェクトはもちろん、Jestからの移行先としても最有力の選択肢です。

比較記事