Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save kody-bot/888c66c3c9c01f9a39029171dce26bb7 to your computer and use it in GitHub Desktop.

Select an option

Save kody-bot/888c66c3c9c01f9a39029171dce26bb7 to your computer and use it in GitHub Desktop.
Why I migrated from Bun Test to Vitest

Why I migrated from Bun Test to Vitest

This is not a "Bun is slow" post. In my case, Bun was often fast. I switched because I stopped trusting the failure modes in the part of my test suite that spawned real processes and local infrastructure.

Context

I was testing a Cloudflare Worker app with a mix of:

  • plain unit tests
  • tests that spawn wrangler dev
  • local mock APIs
  • MSW interception
  • socket/network activity
  • OAuth/MCP integration flows

Originally this all ran under bun test.

What actually went wrong

A few of the root causes were my own test-harness bugs, not Bun bugs:

  • MSW was intercepting loopback traffic for local mock servers without matching passthrough handlers.
  • I had multiple global MSW servers stacking on top of each other in one area of the suite.
  • Some tests were just testing mock infrastructure, and I deleted them instead of preserving them through the migration.

So I do not think it is fair to say Bun caused every failure I saw.

That said, the practical Bun Test pain was still real:

1. Process-heavy tests would hang instead of failing crisply

The most frustrating failures were not normal red/green failures. They were hangs. In CI I would see things like:

  • a test sitting until timeout
  • killed 1 dangling process
  • 60s timeouts before I got useful feedback

That is the class of failure that made me lose confidence in the runner for this suite.

2. It was hard to tell what exactly was leaking

When a test spawns subprocesses, dev servers, or opens sockets, the important question is:

what resource is still alive, and why?

What I wanted was a much more actionable answer than "there was a dangling process". I needed to know whether the issue was:

  • my code
  • the spawned wrangler dev process
  • a socket/server handle
  • the test runner waiting on cleanup
  • some interaction between those pieces

The lack of precise diagnostics made debugging much slower than it needed to be.

3. Long timeout waits made iteration miserable

A big part of the pain was simply waiting around for hangs to surface. Local timeouts were too long for a suite with this kind of failure mode. Even when I fixed some real harness bugs, the developer experience still felt bad because every bad cleanup path cost a lot of time.

4. Bun-specific test/runtime APIs increased coupling

Parts of my suite relied on Bun-specific APIs like:

  • Bun.spawn
  • Bun.serve
  • mock.module
  • Bun.inspect

Those APIs are useful, but they also meant the test harness was coupled to Bun's runtime model. Once I decided I wanted a split-runtime setup, I wasn't just changing test runners, I was rewriting the harness.

Why Vitest felt better for this repo

What I ended up with was a clearer split:

  • node-unit for ordinary Node-friendly tests
  • workers-unit for Cloudflare-specific tests using the Cloudflare Vitest integration
  • mcp-e2e for the expensive integration suite

That gave me a few things I was missing before:

  • an explicit runtime boundary
  • clearer isolation between test categories
  • a better fit for Worker-specific dependencies
  • more confidence that a hung test was the test, not the overall runner setup

I also sped up the slowest MCP integration suite by sharing immutable setup safely: create one seeded database baseline once, then clone it per test. That preserved isolation while cutting that suite from roughly 42s to roughly 12s.

The most useful feedback I would give Bun Test

If the Bun team wants practical feedback from someone who switched away, these would be the top items for me:

  1. Better diagnostics for dangling child processes, sockets, and watchers.
  2. Clearer distinction between "the test timed out" and "the runner is waiting on leaked resources".
  3. More actionable leak reporting at the end of a run.
  4. A stronger story for process-heavy integration tests.
  5. Easier runtime partitioning when part of the suite wants one environment and another part wants something else.

Bottom line

I did not migrate because Bun was obviously slower on raw execution.

I migrated because I needed:

  • more predictable isolation
  • better failure signals
  • cleaner runtime boundaries
  • less time spent debugging hangs

If Bun Test improves the diagnostics and ergonomics around leaked resources and process-heavy integration tests, I would be much more likely to try it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment