コンテンツへスキップ
LatchkeyLatchkey home

Vitest vs node:test: どちらのJSテストランナー?

Vitestは、豊富な機能を備えたVite駆動の高速なテストフレームワークです。node:testは、Nodeに同梱される組み込みで依存関係ゼロのランナーです。

VitestはVite設定とtransformを再利用し、watch modeでテストを並列実行し、JestライクなAPI、モック、coverage、スナップショットテストを提供します - Vite/TSプロジェクトに理想的です。node:testはモダンなNodeに組み込まれ、依存関係を必要とせず、中心的なニーズ(test、サブテスト、node:assertによるアサーション、基本的なモック)をカバーしますが、よりミニマルです。Vitestは機能豊富で、node:testは軽量で依存関係がありません。

What each one actually supports in 2026

Vitestnode:test
依存関係Vitest + Viteなし(組み込み)
機能モック、スナップショット、coverage中心的なテスト機能
TS / Viteファーストクラス手動transform
watch mode高速なHMR基本的
最適な用途Vite/TSアプリ軽量で依存関係ゼロのテスト
watchモードあり (実験的フラグ)あり。HMR風の再実行
TypeScriptネイティブの型除去Vite経由でそのまま
JSXとコンポーネントテストなしVue、React、Svelteなど
ブラウザモードなしあり
CIレポーターspec、tap、dot、junit、lcovを内蔵複数。加えて分散CI向けのシャーディング
ベンチマークなしあり。Tinybench経由
型テストなしあり。expect-type経由
テストタグあり。Node 26.2.0以降フィルタリング経由

CIでの利用

Vitestは、すでにViteを使っているプロジェクトや、coverageとスナップショットを備えたフル機能のランナーが欲しいプロジェクトにフィットし、CIでうまく並列化します。node:testは、テストの依存関係を持ちたくなく高速な起動が欲しいライブラリに魅力的ですが、TSのtransformとcoverage(c8経由)は自分で扱います。アプリの機能の深さにはVitestを、最小限のフットプリントにはnode:testを選びましょう。

  • --test-reporter=junit and =lcov are built in, so CI test reporting and coverage upload need no extra packages.
  • --test-concurrency=N controls parallel test files; concurrency is not an afterthought.
  • --test-randomize with --test-random-seed makes order-dependence reproducible instead of mysterious.
  • --test-rerun-failures <state-file> reruns only what failed, which is a meaningful CI optimisation.
  • --experimental-test-coverage produces V8 coverage without instrumenting your build.
node:test, current capabilities
import { test, describe, it, mock, snapshot } from 'node:test';
import assert from 'node:assert';

// module mocking, not just function mocking
mock.module('./payments.js', {
  namedExports: { charge: mock.fn(() => ({ ok: true })) },
});

// timer mocking
mock.timers.enable({ apis: ['setTimeout', 'Date'] });

describe('checkout', () => {
  it('charges once', async (t) => {
    const spy = t.mock.method(cart, 'total');
    await checkout(cart);
    assert.strictEqual(spy.mock.callCount(), 1);
  });
});

高速化する

依存関係をキャッシュし、テストをジョブ間でシャーディングして実時間を短縮します。どちらもCIランナー上で実行され、より高速なマネージドランナーはテスト実行とtransformのステップを短縮します。

  • Component testing. Vue, React, Svelte and other component tests need a transform pipeline and a DOM. Vitest inherits both from Vite; node:test has neither.
  • Browser mode. Running the suite in a real browser rather than a simulated DOM has no built-in equivalent.
  • JSX and framework transforms. Vitest reuses your existing Vite config, resolvers, and plugins, so tests see the same module graph as your app. Reproducing that under node:test means building it yourself.
  • Benchmarking and type testing. Tinybench-backed benchmarks and expect-type assertions have no built-in counterpart.
  • Sharding. Vitest supports splitting a suite across CI machines natively, which matters once a suite is long enough to need it.
  • Jest compatibility. The Jest-compatible expect and mocking API makes migrating an existing Jest suite mostly mechanical.

判断ルール: 何をテストしているのか?

正直な分かれ目は、機能か依存関係かではありません。テストがブラウザ形の環境とビルドのtransformを必要とするかどうかです。

テスト対象選ぶべきもの理由
公開するnpmライブラリnode:testテスト経路に依存関係がゼロで、リリース周期に追随すべきものもない
バックエンドサービスやCLInode:testDOMもtransformも不要。組み込みの機能で足りる
React、Vue、SvelteのアプリVitestコンポーネントテストにはViteのtransformパイプラインとDOMが要る
すでにViteを使っているものVitestテスト設定が既存のアプリ設定そのもの。2つ目のモジュールグラフを保守しなくてよい
移行したいJestのスイートVitestJest互換のexpectとmockにより、ほぼ機械的に済む
ベンチマークや型テストが要るスイートVitest組み込みの同等物が存在しない
両方があるmonorepo両方runnerの選択はパッケージ単位。混在を禁じる決まりはない

起動コストとCI時間

CIで組み込みrunnerを選ぶ実際的な論拠は、テストを走らせる前にインストールするものが何もないことです。コールドなCI jobでは、テストフレームワークとそのtransformチェーンの npm ci は毎回の実時間であり、アサーションが1つも実行される前に費やされます。

  • node:testはインストールも解決も追加しません。node --test はrunnerにすでにあるNodeで動きます。
  • VitestはVitestとViteとその推移的依存を取得し、初回実行でtransformパイプラインを温めます。
  • 一方でVitestのシャーディングは、大きなスイートの総実時間を、インストール時間の損失をはるかに上回って削減できます。

それぞれをCIで走らせる

GitHub Actions steps
# node:test - nothing to install
- run: node --test --experimental-test-coverage \
    --test-reporter=junit --test-reporter-destination=junit.xml \
    --test-reporter=lcov --test-reporter-destination=lcov.info

# Vitest
- run: npm ci
- run: npx vitest run --coverage --reporter=junit --outputFile=junit.xml

# Vitest, sharded across 4 machines
- run: npx vitest run --shard=${{ matrix.shard }}/4

相互に移行できるか?

部分的に、そして方向が重要です。Vitestからnode:testへは、素の単体テストなら機械的です。describe/it の形は同じで、主な作業は expect(...) のアサーションを node:assert に書き換え、モック呼び出しを対応付け直すことです。コンポーネントテストの移行は書き換えではなく作り直しになります。transformパイプラインとDOMに組み込みの同等物がないからです。

  • アサーション: expect(a).toBe(b)assert.strictEqual(a, b) になります。ほぼ検索置換ですが、深い等価性には注意が要ります。
  • モック: vi.fn()mock.fn()vi.mock()mock.module() になりますが、APIは同一ではありません。
  • スナップショット: どちらにもありますが、ファイル形式が異なるため、移植ではなく再生成を見込んでください。
  • コンポーネントテスト: 経路はありません。これらはVitestに残ります。

The switching cost is mostly in the parts nobody lists

  • Assertions and mocks usually port mechanically when the target implements a compatible API; custom transformers and framework plugins do not.
  • Snapshot formats differ between runners, so plan to regenerate and review rather than port.
  • Run both suites in parallel in CI for a period and diff the results. A migration that changes which tests fail is not a migration, it is a regression you have not found yet.
  • Coverage numbers move on a runner change even when the tests do not, because instrumentation differs. Re-baseline any coverage gate deliberately.

The verdict

ViteまたはTypeScriptアプリを構築し、モック、スナップショット、coverageを標準で欲しい場合: Vitest。依存関係ゼロと高速な起動を重視するライブラリを書く場合: node:test。ほとんどのアプリチームはVitestを選び、ミニマリストやライブラリ作者はますますnode:testを使います。

よくある質問

Node組み込みのテストrunnerは本番で使えますか?
はい。node:test はNode 20以降Stableとされており、実験的ではありません。スナップショットテストはv22.3.0で安定し、グローバルのsetupとteardownはv24で入りました。まだ実験的フラグの背後にあるのはコードカバレッジで、--experimental-test-coverage を使います。
node:testはモックに対応していますか?
はい、しかも多くの比較が示すよりも本格的に対応しています。関数、メソッド、プロパティ、getterとsetter、そして mock.module() によるモジュール全体をモックでき、さらに mock.timerssetTimeoutsetIntervalDate を含むタイマーも扱えます。
node:testはTypeScriptを実行できますか?
はい。Nodeはネイティブに型を除去し、--no-strip-types を渡すと既定のテストファイルパターンが .ts.mts.cts を含むよう拡張されます。素直なTypeScriptなら別途transformは不要ですが、Viteのpluginやパスエイリアスに依存するものは依然として設定が要ります。
Vitestはnode:testより速いですか?
どこを測るかによります。node:testはフレームワークのインストールと解決を回避するため、小さなスイートでは総CI実時間でたいてい勝ちます。Vitestはマシン間のシャーディングに対応し、スイートが大きくなれば決定的に勝ちます。仮定せず、インストールと実行の内訳を測ってください。
node:testをReactのコンポーネントテストに使えますか?
実用的には無理です。コンポーネントテストにはDOMとJSXのtransformパイプラインが要りますが、node:testはどちらも提供しません。VitestはViteから両方を継承します。コンポーネントをテストするなら、それだけでこの比較の結論が決まります。
node:testはCIのテストレポートやカバレッジツールと連携できますか?
はい。junitlcov のレポーターを同梱しているため、テストレポート用のJUnit XMLとカバレッジアップロード用のlcovが追加パッケージなしで動きます。これはかつてCI上で最も強かった反対論拠を取り除きます。
Vitestからnode:testへどう移行しますか?
素の単体テストならほぼ機械的です。describe/it の構造はそのまま引き継がれ、expect(...) のアサーションは node:assert の呼び出しに、vi.fn()/vi.mock()mock.fn()/mock.module() になります。スナップショットの形式は異なるので再生成を見込んでください。コンポーネントテストは移行できず、Vitestに残すべきです。
ライブラリ作者はVitestとnode:testのどちらを使うべきですか?
多くの場合node:testです。公開ライブラリは、テスト経路に最新へ追随すべきものがないこと、利用者側の構成に対してデバッグすべきtransformチェーンがないこと、追いかけるべきフレームワークのリリース周期がないことから利益を得ます。組み込みrunnerは、ライブラリのテストスイートが通常必要とするものを今やカバーしています。

関連ガイド