Plate 47
bun test vs Vitest: 136ms vs 1.67s on 300 Tests
bun test 1.4.2 vs Vitest 5.0.3 on 300 TypeScript tests in 60 files: warm wall medians 136 ms vs 1,673 ms (~12x); Vitest --no-isolate 765 ms (~5.6x). A 5-file vitest-API check (vi.fn, vi.mock, fake timers, spyOn, it.each, snapshot) passed 7/7 under Bun.
Aditya Challa5 min read
I ran the same 300 TypeScript unit tests in 60 files under bun test 1.4.2 and Vitest 5.0.3: bun test finished in a 136 ms wall-clock median, Vitest took 1.67 s with default isolation and 765 ms with --no-isolate. That is about 12x against default Vitest and about 5.6x against the fairer no-isolate setting, and all 300 tests passed under both.
Short answer: try bun test for plain Node-style unit suites where you do not lean on Vite plugins or a DOM environment. Stay on Vitest when your tests depend on vitest.config transforms, aliases, browser mode, or coverage you already trust. No affiliate links in this post.
What I tested
- Machine: 8 vCPU Intel Xeon / 15 GB RAM shared Linux cloud box, tested 5 Oct 2026 (about 23:59 IST to 00:05 IST on 6 Oct).
- Versions: Bun 1.4.2, Vitest 5.0.3 on Node 22.20.0. Vitest 5 declares Node ^22.12, ^24 or 26+, so Node 20 is out.
- Fixture: /workspace/bench-bun-vitest/. 60 source modules in TypeScript, 60 test files, 5 tests each (toHaveLength, toEqual, toBeGreaterThan, an async resolves check with a 1 ms timer, toStrictEqual). Vitest config: environment node, globals on.
- Timing: wall clock around each process, called directly (no npx). One warm-up, then 9 interleaved rounds. A separate cold series of 5 cleared node_modules/.vite and node_modules/.vitest before each Vitest run and set BUN_RUNTIME_TRANSPILER_CACHE_PATH=0 for Bun.
- Findings: warm medians bun test 136 ms, Vitest 1,673 ms, Vitest --no-isolate 765 ms. Cold medians 135 ms and 1,728 ms. Clearing caches barely moved either tool.
- One surprise: a separate 5-file check that imports from "vitest" (vi.fn, vi.mock, vi.useFakeTimers, vi.spyOn, it.each, toMatchSnapshot) passed 7/7 under bun test with no edits.
- Not tested: jsdom or happy-dom, coverage, Vitest browser mode, watch-mode reruns, path aliases or Vite plugins, suites with heavy real I/O, Windows or macOS.
Is bun test faster than Vitest on the same suite?
| Run (300 tests, 60 files) | bun test 1.4.2 | Vitest 5.0.3 default | Vitest 5.0.3 --no-isolate |
|---|---|---|---|
| Warm wall median (9 runs) | 136 ms | 1,673 ms | 765 ms |
| Cold wall median (5 runs, caches cleared) | 135 ms | 1,728 ms | not run |
| Tests passed | 300/300 | 300/300 | 300/300 |
| Runtime needed | Bun binary only | Node 22.12+ | Node 22.12+ |
Yes, by a wide margin on this fixture. Vitest itself printed why the default is slow here: it spawned 60 workers at roughly 95 ms of startup each, one per file. Bun runs the files in one process, so the closest Vitest setting is --no-isolate, and the gap there is still about 5.6x.
If you are still choosing between Jest and Vitest, my Jest vs Vitest numbers show the same isolation cost from the other side. The Vitest 5 upgrade post covers the Node 22 engine gate in more detail.
Will my Vitest tests run under bun test without edits?
The basic ones did. My compat files used vi.fn with mock.results, a hoisted vi.mock of a local module, fake timers with advanceTimersByTime, vi.spyOn with mockReturnValue, it.each, and a file snapshot. Bun passed all 7 tests and Vitest passed the same 7.
Where I would expect trouble: anything configured in vitest.config, because bun test does not read it. Aliases, setupFiles, Vite plugins such as Vue or Svelte transforms, and environment: jsdom all need Bun-side equivalents. My jsdom vs happy-dom test is the place to start if your suite needs a DOM.
Does isolation explain the whole gap?
No. Turning isolation off halved Vitest's time, from 1,673 ms to 765 ms, but Bun was still about 630 ms faster. The rest is Vite's transform pipeline and Node worker startup versus Bun's built-in TypeScript transpiler.
The trade-off is real. With one process, a test that mutates a global or leaves a module mock behind can leak into the next file. Vitest's default isolation exists to stop that. If your suite has a history of order-dependent failures, the 12x number overstates what you can safely bank.
Should you switch from Vitest to bun test?
| Situation | My pick |
|---|---|
| Pure TS or JS unit tests, no DOM, no Vite plugins | Try bun test |
| Already using Bun as the package manager or runtime | bun test |
| Vue, Svelte or other Vite-plugin transforms in tests | Stay on Vitest |
| jsdom or happy-dom component tests | Stay on Vitest for now |
| Coverage thresholds wired into CI | Stay on Vitest until you re-check coverage numbers |
| Stuck on Node 20 | bun test (Vitest 5 needs Node 22.12+) |
Bottom line: 136 ms against 1.67 s is a real difference in a tight edit-and-rerun loop, and the API overlap was better than I expected. Who should not switch yet: teams whose tests depend on vitest.config, browser mode, or DOM environments, because none of that carries over. For install speed with Bun versus pnpm and npm, see pnpm vs npm vs Bun; for what changed in the runtime itself, see the Bun 1.4.2 notes.
How this was made: I generated a 60-file TypeScript suite on the ShopperCove test box, timed interleaved bun test and Vitest runs, ran a small vitest-API compat check under Bun, and kept the JSON; the write-up was drafted with AI help and checked against that output.
Sources
- https://bun.sh/docs/cli/test
- https://bun.sh/docs/test/writing
- https://vitest.dev/guide/
- https://vitest.dev/config/
- https://www.npmjs.com/package/vitest
- https://github.com/oven-sh/bun
Related
- https://www.shoppercove.com/blog/jest-vs-vitest
- https://www.shoppercove.com/blog/vitest-5-should-you-upgrade-october-2026
- https://www.shoppercove.com/blog/jsdom-vs-happy-dom-vitest
- https://www.shoppercove.com/blog/pnpm-vs-npm-vs-bun
- https://www.shoppercove.com/blog/bun-1-4-2-elysia-als-cmyk-jpeg-october-2026
- https://www.shoppercove.com/blog/c8-vs-nyc
- https://www.shoppercove.com/blog/msw-vs-nock
- https://www.shoppercove.com/blog/tsx-vs-ts-node
Lab evidence
What I found running this
Hands-on on ShopperCove box 5 Oct 2026 ~23:59 IST to 6 Oct ~00:05 IST (8 vCPU Intel Xeon / 15 GB shared Linux). Bun 1.4.2; Vitest 5.0.3 on Node 22.20.0 (Vitest 5 engines Node ^22.12 || ^24 || >=26). Fixture: /workspace/bench-bun-vitest/ - 60 TS modules + 60 test files x 5 tests, environment node, globals. Direct binaries, no npx. 1 warm-up + 9 interleaved rounds: warm medians bun 136 ms, vitest 1673 ms, vitest --no-isolate 765 ms. 5 cold rounds (cleared node_modules/.vite + .vitest; BUN_RUNTIME_TRANSPILER_CACHE_PATH=0): 135 ms vs 1728 ms. All 300/300 pass. Compat dir (5 files importing from vitest) passed 7/7 under bun test and Vitest. Raw: /workspace/bench-bun-vitest/res-bun-vitest.json (npx-inclusive first run in res-bun-vitest-npx.json). Not tested: jsdom/happy-dom, coverage, browser mode, watch, aliases/Vite plugins, heavy I/O, Windows/macOS. No affiliate.
Related links
Plate 46
esbuild-register vs jiti: 343ms vs 540ms Cold
esbuild-register 3.6.0 vs jiti 2.7.0 on a 61-file TypeScript graph: cold wall medians 343 ms vs 540 ms (~1.57x); after jiti disk cache warm, 185 ms vs esbuild-register 350 ms. Same printed result 970.
5 Oct 2026
Plate 21
Vitest 5.0.3 vs 4.1.10: Should You Upgrade? (20-File Timing)
Vitest 5 upgrade decision with ShopperCove timing: 20-file forks suite median ~512 ms on 5.0.3 vs ~547 ms on 4.1.10 (Node 22.20.0). Engines require Node 22.12+ and Vite 6.4+. clearMocks defaults true. No affiliate.
5 Oct 2026
Plate 69
oxlint vs ESLint: Should You Switch? 9.8s vs 0.09s
oxlint 1.87.0 vs ESLint 9.39.4 on react-hook-form (301 files): 0.09 s vs 9.81 s single-thread (6.02 s with --concurrency auto, 1.10 s warm --cache). @oxlint/migrate ported 42 of 84 rules natively, 80 with JS plugins. 315 vs 562 warnings; seeded test 11/15 native, 13/15 with JS plugins, ESLint 15/15.
5 Oct 2026