← Semua picks

Tool Review Recommended

Vitest vs Bun test vs Jest: test runner 2026

3 test runner JavaScript. Vitest = ESM-first compat, Bun = native+cepat, Jest = legacy de facto. Saya pakai keduanya, verdict tergantung runtime.

20 Mei 2026 · 7 menit
VitestBunJest

TL;DR

  • Vitest: Recommended untuk Node project, ekosistem Jest-compatible
  • Bun test: Recommended untuk Bun runtime project, native + super cepat
  • Jest: Skip untuk project baru, only maintain legacy

Konteks

3 project saya 2024-2026:

  • SaaS billing fotograf (Node + Hono): Vitest
  • SaaS klinik dental (Bun + Hono): Bun test
  • Internal tool Laravel + React: Vitest (untuk React frontend)

Performance benchmark

Same test suite: 120 test cases (unit + integration), TypeScript, mock fetch.

RunnerCold runHot run (watch)
Jest8.4s2.8s
Vitest2.1s0.4s
Bun test0.9s0.2s

Bun test paling cepat (native). Vitest signifikan lebih cepat dari Jest karena ESM-first + Vite-based.

Vitest

Pro

  • Jest-compatible API: 95% Jest test bisa run di Vitest tanpa change
  • ESM-first: tidak butuh @babel/preset-env + CommonJS dance
  • Vite ecosystem: kalau Anda pakai Vite (or Astro), config shared
  • Watch mode: instant re-run via Vite HMR
  • UI mode: visual test runner (run via vitest --ui)
  • Active development: weekly release, fix bug fast

Con

  • Bundle size lebih besar dari Bun test
  • TypeScript support OK tapi butuh tsconfig sync
  • Mock API slightly less elegant dari Bun test

Example

import { describe, it, expect, vi } from 'vitest';
import { calculateBill } from './bill';

describe('calculateBill', () => {
  it('compute tax correctly', () => {
    expect(calculateBill(100, 0.11)).toBe(111);
  });

  it('handles network error', async () => {
    const fetch = vi.spyOn(globalThis, 'fetch').mockRejectedValue(new Error('Network'));
    await expect(getRates()).rejects.toThrow('Network');
  });
});

Bun test

Pro

  • Native ke Bun runtime: tidak butuh dependency tambahan
  • Paling cepat: 2-3x faster dari Vitest
  • Same syntax as Jest/Vitest: minimal learning curve
  • Built-in coverage (via bun test --coverage)
  • Snapshot test built-in

Con

  • Bun-only: tidak run di Node CI yang tidak install Bun
  • Ecosystem kecil: less third-party reporter, plugin, integration
  • TypeScript config kadang issue: subtle behavior differences dari tsc

Example

import { describe, it, expect, mock } from 'bun:test';
import { calculateBill } from './bill';

describe('calculateBill', () => {
  it('compute tax correctly', () => {
    expect(calculateBill(100, 0.11)).toBe(111);
  });

  it('handles network error', async () => {
    const fetch = mock(() => Promise.reject(new Error('Network')));
    globalThis.fetch = fetch;
    await expect(getRates()).rejects.toThrow('Network');
  });
});

Hampir identical dengan Vitest. Migration trivial.

Jest

Pro

  • Most mature: 10+ tahun, paling banyak ecosystem
  • Familiar: most React tutorial pakai Jest
  • Stable: bug rare, behavior predictable

Con

  • Slow di 2026: 4-10x lebih lambat dari Vitest/Bun
  • CommonJS-first: butuh transform untuk ESM modern code
  • Config kompleks: babel + jest.config.js + transformers
  • Stagnant innovation: feature baru release lambat

Migration

Jest → Vitest

bun remove jest @types/jest babel-jest jest-environment-jsdom
bun add -d vitest @vitest/ui happy-dom

Update package.json:

{
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "coverage": "vitest --coverage"
  }
}

Create vitest.config.ts:

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'happy-dom',  // atau 'node' atau 'jsdom'
    globals: true,
  },
});

Update test files: change from 'jest'from 'vitest'. Most other API same.

Time: 1-2 jam untuk 100-200 test file.

Vitest → Bun test

# Remove vitest
bun remove vitest @vitest/ui happy-dom

# Update import
# from 'vitest' → from 'bun:test'

# Update test script
# "test": "vitest" → "test": "bun test"

Bun test cover most Vitest API. Some edge case (custom matchers, advanced mock) butuh adjust.

Time: 30-60 menit untuk simple suite, 2-4 jam untuk complex.

Common pain point

Mocking external module

Jest: jest.mock('./module', () => ({ ... })). Powerful but complex.

Vitest: vi.mock('./module', () => ({ ... })). Similar to Jest.

Bun test: mock.module('./module', () => ({ ... })). Newer API, occasional bug.

Untuk complex mocking: Vitest lebih mature dari Bun test.

TypeScript path alias

Vitest read tsconfig automatically. Bun test sometimes butuh manual path setup di bunfig.toml.

CI integration

GitHub Actions:

# Vitest
- uses: actions/setup-node@v4
  with: { node-version: 22 }
- run: bun install && bun run test

# Bun test  
- uses: oven-sh/setup-bun@v2
- run: bun install && bun test

Both straightforward.

Decision matrix

Project typeRecommended
Bun runtime (Hono, Elysia)Bun test
Node runtime (Express, NestJS)Vitest
Vite/Astro projectVitest
Legacy Jest projectVitest (migrate) atau Jest (stay)
Performance-critical CIBun test

Konteks Indonesia

Untuk SMB Indonesia 2026:

  • Most project saya pakai Bun runtime → Bun test default
  • Untuk klien dengan team Node existing → Vitest (less migration friction)
  • Jangan default Jest untuk project baru (legacy choice di 2026)

Yang surprising

Bun test cepat banget di CI. Saya migrate 1 project klien dari Vitest ke Bun test → CI time turun dari 4.2 menit ke 1.8 menit. Saving signifikan untuk team yang push 20+ PR per minggu.

Vitest watch mode under Vite HMR sangat smooth — test re-run dalam 100-300ms saat file change. Untuk TDD workflow, Vitest still excellent.

Verdict

Recommended:

  • Bun test untuk Bun runtime project (default jika Anda commit ke Bun)
  • Vitest untuk Node runtime atau Vite/Astro project

Skip:

  • Jest untuk new project (legacy choice)

Pilih based on runtime + ecosystem fit. Migration cost rendah, jadi tidak perlu over-think.

Ditulis oleh Asti Larasati

// Pick Tool Review lain


← Semua picks RSS feed