Vitest
Vite ベースの高速なテストフレームワークで、Jest 互換の API を提供する
Vitest とは
Vitest は、Vite のビルドパイプラインを活用した高速なテストフレームワークで、Jest 互換の API (describe, it, expect) を提供する。ESM ネイティブ対応、TypeScript のゼロコンフィグサポート、HMR によるウォッチモードの高速化が特徴だ。最初の公開版は 2021 年 12 月、安定版の 1.0 は 2023 年 12 月に出た。2026 年 8 月時点の最新メジャーは 2025 年 10 月公開の 4 系で、Jest からの移行先として定着している。
速さの出どころは Vitest 自身ではなく Vite 側にある。Vitest は vite.config.ts をそのまま読み、resolve.alias やプラグインといった変換設定を開発サーバーと共有する。テスト専用のトランスパイル設定を書かずに、アプリと同じ解決規則でテストが走るのはこの仕組みによる (Vite そのものの構造は Vite を参照)。裏返せば依存の向きも一方向で、Vite の設定を壊すとテストも同時に壊れる。
Jest との比較
Jest との主な違いを比較する。Jest 側は 30 系 (2025 年 6 月公開) の状況で、いずれも 2026 年 8 月時点の記述だ。
| 観点 | Vitest | Jest |
|---|---|---|
| 速度 | 高速 (Vite の変換パイプライン) | 中程度 (Babel/ts-jest) |
| ESM 対応 | ネイティブ | 実験的 (--experimental-vm-modules) |
| TypeScript | ゼロコンフィグ | ts-jest or @swc/jest が必要 |
| 設定 | vite.config.ts を共有 | jest.config.ts が別途必要 |
| ウォッチモード | HMR で変更ファイルのみ再実行 | ファイル単位で再実行 |
| API 互換性 | Jest 互換 | 互換の基準となる側。移行で直すのは主に設定ファイル |
基本的な使い方
テストファイルは *.test.ts のように名前で見分ける規約で、既定の探索パターンに合っていれば対象コードの隣に置いてよい。
// sum.ts
export function sum(a: number, b: number): number {
return a + b;
}
// sum.test.ts
import { describe, it, expect } from 'vitest';
import { sum } from './sum';
describe('sum', () => {
it('adds two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
it('handles negative numbers', () => {
expect(sum(-1, 1)).toBe(0);
});
});
npx vitest # ウォッチモード (開発中)
npx vitest run # 1 回実行 (CI)
npx vitest --coverage # カバレッジ付き
モック
モック関連の関数は vi 名前空間に集約されている。jest.* とほぼ 1 対 1 に対応するが、同名で挙動が違うものがあるため後述の非互換点と併せて読む必要がある。
import { vi, describe, it, expect } from 'vitest';
// 関数のモック
const mockFn = vi.fn().mockReturnValue(42);
// モジュールのモック
vi.mock('./db', () => ({
findUser: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
}));
// タイマーのモック
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.useRealTimers();
vitest.config.ts
設定は vite.config.ts の test フィールドに書いてもよい。テストだけ別の設定にしたいときに vitest.config.ts を置き、この場合はそちらが vite.config.ts の設定を上書きする。
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true, // describe, it, expect をグローバルに
environment: 'node', // 'node' | 'jsdom' | 'happy-dom'
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
thresholds: { lines: 80, branches: 80 },
},
include: ['src/**/*.test.ts'],
},
});
Jest からの移行
API が Jest 互換のため、移行は比較的容易だ。
jest→vitestに依存を変更jest.config.ts→vitest.config.tsに設定を移行jest.fn()→vi.fn()に置換jest.mock()→vi.mock()に置換
多くのテストはこの置換で動くが、名前が同じでも意味が違う箇所が残る。公式の移行ガイド (2026 年 8 月時点) が挙げる主な差分は次の 4 点だ。
- グローバル API: Jest は
describeやexpectを既定でグローバルに置くが、Vitest は置かない。globals: trueを設定するかvitestから import する。無効のままにすると testing-library の DOM 自動クリーンアップが働かない副作用がある。 mockResetの意味: Jest は実装を undefined を返す空関数へ差し替える。Vitest はvi.fn(impl)に渡した元の実装へ戻す。差し替えを期待したテストは静かに緩くなる。- モジュールモックの戻り値: Jest はファクトリの戻り値を default export として扱う。Vitest は export ごとにキーを明示したオブジェクトを返す必要がある。
- 実物の読み込みとタイムアウト:
jest.requireActualは非同期のawait vi.importActualへ、jest.setTimeoutはvi.setConfig({ testTimeout })へ変わる。doneコールバック形式のテストは書けないため、async 関数か Promise を返す形へ書き換える。
ブラウザモードと型テスト
実 DOM を使うテストは「ブラウザモード」が担う。4 系 (2025 年 10 月) で実験的の位置づけが外れ、実行基盤を @vitest/browser-playwright のようなプロバイダー別パッケージとして入れる形に変わった。同じ 4 系でスクリーンショットを比較する toMatchScreenshot も加わり、視覚回帰テストが公式機能になっている。
一方、型の検査は通常のテスト実行に含まれない。変換は型注釈を落とすだけなので、--typecheck を付けて型検査を有効にしない限り型エラーでテストは落ちない。速さと引き換えの割り切りであり、CI では型検査を独立したステップとして残す構成が要る。
Vitest を扱う関連書籍も多い。
この記事は役に立ちましたか?