vitest vs jest 徹底比較

vitest の詳細jest の詳細
AI生成コンテンツ

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

Vitest vs Jest — 2025年版 JavaScript テストフレームワーク徹底比較

1. 結論

Vite ベースのプロジェクトなら Vitest を選ぶのが最適解です。 既存の大規模プロジェクトで Jest 資産が豊富にある場合は、無理に移行せず Jest を継続するのが現実的です。新規プロジェクトであれば、速度・DX(開発者体験)の面から Vitest を第一候補にすることをおすすめします。


2. 比較表

観点VitestJest
初回リリース2022年2014年
最新バージョン(2025年6月時点)v3.xv30.x
npm 週間DL数約 1,200万約 3,500万
バンドラー / トランスフォーマーVite (esbuild / Rollup)babel-jest(デフォルト)
ESM ネイティブ対応✅ 完全対応⚠️ 実験的(--experimental-vm-modules
TypeScript 対応✅ 設定不要で動作⚠️ ts-jest or @swc/jest が必要
設定ファイルvitest.config.ts(Vite 設定と統合可)jest.config.ts
Watch モード速度⚡ 非常に高速(HMR ベース)普通
ブラウザテスト@vitest/browser で対応❌ jsdom / happy-dom のみ
スナップショットテスト✅ Jest 互換✅ 本家
モックvi オブジェクト(Jest 互換 API)jest オブジェクト
カバレッジ@vitest/coverage-v8 / istanbul--coverage(istanbul / v8)
UI@vitest/ui(ブラウザ UI)❌ なし(サードパーティ)
並列実行✅ ワーカースレッド / マルチプロセス✅ ワーカープロセス
インラインテストif (import.meta.vitest) で可能❌ 非対応
エコシステム・情報量急成長中非常に豊富
学習コスト低い(Jest 経験者ならほぼゼロ)低い(デファクト標準)
Jest からの移行コスト低い(API 互換レイヤーあり)

3. それぞれの強み

Vitest の強み

  • 圧倒的な速度: Vite の HMR パイプラインを活用し、変更ファイルのみを高速に再実行します。大規模プロジェクトほど差が顕著です。
  • ESM ファースト: import / export をネイティブに扱えるため、ESM 移行済みのコードベースでハック的な設定が不要です。
  • TypeScript がゼロコンフィグ: esbuild による型ストリップが内蔵されており、ts-jest のような追加パッケージが要りません。
  • Vite 設定との統合: vite.config.tstest ブロックを追加するだけで済み、エイリアスやプラグインを二重管理する必要がありません。
  • インラインテスト: ソースファイル内にテストを書ける独自機能があり、ユーティリティ関数のコロケーションに便利です。
  • ブラウザモード: Playwright や WebDriverIO と連携し、実際のブラウザ上でテストを実行できます。

Jest の強み

  • 圧倒的な実績と情報量: 10年以上の歴史があり、Stack Overflow・ブログ記事・書籍の情報が桁違いに豊富です。
  • 巨大なエコシステム: jest-extendedjest-domjest-image-snapshot など、サードパーティマッチャーやプラグインが充実しています。
  • フレームワーク公式サポート: Create React App、Angular CLI、NestJS など多くのフレームワークがデフォルトで Jest を採用しています。
  • 安定性: メジャーバージョン間の破壊的変更が少なく、長期運用プロジェクトで安心感があります。
  • --shard による CI 分散: 大規模 CI で複数マシンにテストを分散する仕組みが成熟しています(※ Vitest にも同機能あり)。

4. コード例で比較

題材: 非同期関数 fetchUser のテスト

まず、テスト対象のコードです。

// src/fetchUser.ts
export interface User {
  id: number;
  name: string;
}

export async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`https://api.example.com/users/${id}`);
  if (!res.ok) throw new Error(`User ${id} not found`);
  return res.json();
}

Vitest で書いた場合

// src/__tests__/fetchUser.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { fetchUser } from '../fetchUser';

describe('fetchUser', () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  it('ユーザーを正常に取得できる', async () => {
    const mockUser = { id: 1, name: 'Taro' };

    // グローバル fetch をモック
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve(mockUser),
    }));

    const user = await fetchUser(1);

    expect(user).toEqual(mockUser);
    expect(fetch).toHaveBeenCalledWith('https://api.example.com/users/1');
  });

  it('404 の場合はエラーを投げる', async () => {
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: false,
      status: 404,
    }));

    await expect(fetchUser(999)).rejects.toThrow('User 999 not found');
  });
});

実行コマンド:

npx vitest run
# Watch モード
npx vitest

Jest で書いた場合

// src/__tests__/fetchUser.test.ts
import { fetchUser } from '../fetchUser';

describe('fetchUser', () => {
  beforeEach(() => {
    jest.restoreAllMocks();
  });

  it('ユーザーを正常に取得できる', async () => {
    const mockUser = { id: 1, name: 'Taro' };

    // グローバル fetch をモック
    jest.spyOn(global, 'fetch').mockResolvedValue({
      ok: true,
      json: () => Promise.resolve(mockUser),
    } as Response);

    const user = await fetchUser(1);

    expect(user).toEqual(mockUser);
    expect(global.fetch).toHaveBeenCalledWith('https://api.example.com/users/1');
  });

  it('404 の場合はエラーを投げる', async () => {
    jest.spyOn(global, 'fetch').mockResolvedValue({
      ok: false,
      status: 404,
    } as Response);

    await expect(fetchUser(999)).rejects.toThrow('User 999 not found');
  });
});

実行コマンド:

npx jest
# Watch モード
npx jest --watch

コード比較のポイント

観点VitestJest
import 文vi を明示的に importグローバル jest が自動注入
モック関数vi.fn() / vi.stubGlobal()jest.fn() / jest.spyOn()
API の互換性Jest とほぼ同一
TypeScript 実行追加設定なしts-jest or @swc/jest が必要

ご覧のとおり、テストコード自体はほぼ同じです。jestvi に置き換えるだけで動くケースが大半であり、移行コストの低さが Vitest の大きな魅力です。


5. どちらを選ぶべきか — ユースケース別ガイド

✅ Vitest を選ぶべきケース

ユースケース理由
Vite / Nuxt 3 / SvelteKit プロジェクトVite 設定をそのまま共有でき、エイリアスやプラグインの二重管理が不要
新規プロジェクト(フレームワーク問わず)ESM ネイティブ・TypeScript ゼロコンフィグの恩恵が大きい
テスト実行速度を改善したいHMR ベースの Watch モードで開発ループが劇的に速くなる
ブラウザ環境での実テストが必要@vitest/browser で Playwright 連携が可能

✅ Jest を選ぶべきケース

ユースケース理由
既存の大規模 Jest 資産があるカスタムマッチャー・設定・CI パイプラインの移行コストが高い
Angular / NestJS プロジェクトCLI がデフォルトで Jest を生成し、公式ドキュメントも Jest 前提
チームに Jest 経験者が多い学習コストゼロで即戦力になる
サードパーティ連携が必須特定の Jest プラグインに依存している場合

🔄 どちらでもよいケース

  • React(Next.js 以外)の新規プロジェクト → Vitest 推奨(ただし Jest でも問題なし)
  • Next.js → 公式が @next/jest を提供しているため Jest が安定。ただし Vitest 対応も進んでおり、どちらも選択可能

6. まとめ

Jest  = 安定・実績・情報量のデファクト標準
Vitest = 速度・DX・モダン設計の次世代テストランナー

Vitest は Jest の API を意図的に踏襲して設計されているため、「Jest の正統進化版」 と捉えるのが最も正確です。既存プロジェクトで Jest が問題なく動いているなら急いで移行する必要はありません。しかし、新規プロジェクトや Vite エコシステムを採用しているなら、Vitest を選ばない理由を探す方が難しいでしょう。

どちらを選んでも「テストを書く」という本質は変わりません。大切なのはフレームワーク選定に時間をかけすぎず、テストを書き続けることです。