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
| Vitest | node: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=junitand=lcovare built in, so CI test reporting and coverage upload need no extra packages.--test-concurrency=Ncontrols parallel test files; concurrency is not an afterthought.--test-randomizewith--test-random-seedmakes order-dependence reproducible instead of mysterious.--test-rerun-failures <state-file>reruns only what failed, which is a meaningful CI optimisation.--experimental-test-coverageproduces V8 coverage without instrumenting your build.
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
expectand mocking API makes migrating an existing Jest suite mostly mechanical.
判断ルール: 何をテストしているのか?
正直な分かれ目は、機能か依存関係かではありません。テストがブラウザ形の環境とビルドのtransformを必要とするかどうかです。
| テスト対象 | 選ぶべきもの | 理由 |
|---|---|---|
| 公開するnpmライブラリ | node:test | テスト経路に依存関係がゼロで、リリース周期に追随すべきものもない |
| バックエンドサービスやCLI | node:test | DOMもtransformも不要。組み込みの機能で足りる |
| React、Vue、Svelteのアプリ | Vitest | コンポーネントテストにはViteのtransformパイプラインとDOMが要る |
| すでにViteを使っているもの | Vitest | テスト設定が既存のアプリ設定そのもの。2つ目のモジュールグラフを保守しなくてよい |
| 移行したいJestのスイート | Vitest | Jest互換の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で走らせる
# 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はモックに対応していますか?
mock.module() によるモジュール全体をモックでき、さらに mock.timers で setTimeout、setInterval、Date を含むタイマーも扱えます。node:testはTypeScriptを実行できますか?
--no-strip-types を渡すと既定のテストファイルパターンが .ts、.mts、.cts を含むよう拡張されます。素直なTypeScriptなら別途transformは不要ですが、Viteのpluginやパスエイリアスに依存するものは依然として設定が要ります。Vitestはnode:testより速いですか?
node:testをReactのコンポーネントテストに使えますか?
node:testはCIのテストレポートやカバレッジツールと連携できますか?
junit と lcov のレポーターを同梱しているため、テストレポート用のJUnit XMLとカバレッジアップロード用のlcovが追加パッケージなしで動きます。これはかつてCI上で最も強かった反対論拠を取り除きます。Vitestからnode:testへどう移行しますか?
describe/it の構造はそのまま引き継がれ、expect(...) のアサーションは node:assert の呼び出しに、vi.fn()/vi.mock() は mock.fn()/mock.module() になります。スナップショットの形式は異なるので再生成を見込んでください。コンポーネントテストは移行できず、Vitestに残すべきです。