Replacing Jest with `bun test`: A Practical Migration Guide
Bun ships with a built-in test runner that supports Jest-style APIs out of the box. Here is how to evaluate `bun test` as a replacement for external frameworks in Node-compatible projects, including a worked example and the limitations to check first.
06 Dec 2025, 08:05 UTC

Maintaining a Node.js project often means carrying a test framework that is almost as heavy as the application itself. Jest, Vitest, and their TypeScript companions add dozens of transitive dependencies, bespoke configuration files, and a separate transpilation step—converting TypeScript to JavaScript—before a single assertion runs. If you are already evaluating Bun as a runtime, its built-in test runner, invoked with bun test, can collapse that stack into a single binary. The practical question is not whether bun test is fast, but whether it is compatible enough with your existing suite to remove Jest or Vitest from your development dependencies without rewriting every test.
Thesis: bun test is a strong default for new Bun projects and a viable migration target for Node-compatible codebases, provided you verify matcher parity—that the assertion helpers you rely on behave the same way—and pin the Bun version in CI.
What bun test actually replaces
Bun ships with a test runner built into the runtime executable. It exposes Jest-style globals and imports—test, it, describe, and expect—so many existing test files can run without importing a separate framework package. TypeScript, JSX, and JSON modules are handled by Bun's native transpiler, which means you do not need ts-jest or a Babel pipeline just to resolve imports in test files.
Because the runner is part of the Bun binary, a CI job that previously installed Node.js, Jest, and a TypeScript transformer can instead install only Bun. That reduces both the dependency tree and the configuration surface: no jest.config.js, no vitest.config.ts, and no transformer plugin list are required for basic discovery.
Default discovery and module resolution
Out of the box, bun test looks for files matching *.test.ts, *.test.js, *.spec.ts, and *.spec.js. In a typical project this means you can delete the framework configuration and run the command from the repository root without extra flags. The runner uses Bun's module resolution rather than Node.js require extension rules, so imports that work in your application code generally work in tests.
One caveat: if your project relies on Node.js internals—certain module mock patterns, custom environment globals, or setup files that patch Node-specific behavior—those may need rewriting. Bun's runtime is Node-compatible, but it is not a drop-in replacement for every Jest setup file.
Worked example: replacing a Jest test
Suppose you have a plain arithmetic test in src/math.test.ts that currently runs under Jest:
// src/math.test.ts
import { test, expect } from "bun:test";
test("adds two numbers", () => {
expect(1 + 1).toBe(2);
});
To verify this locally, open a terminal in your project root—no elevated permissions are required—and run:
cd <project-root>
bun test src/math.test.ts
If the assertion passes, the command exits with code 0. If it fails, you will see a non-zero exit code and a diff. Next, remove the path argument to confirm default discovery picks up the file:
bun test
If the file is discovered and executed, the basic migration path works for this test. For a suite with many files, repeat this check on a representative sample before removing Jest from package.json.
Trade-offs and pre-adoption checks
The biggest risk is assuming full Jest parity. Some Jest-specific matchers, mocking APIs, coverage thresholds, and watch plugins may behave differently or be unsupported. Snapshot formats and coverage reports often differ from Istanbul-based tools—the coverage output format used by Jest and many Node.js pipelines—which can break CI parsers that expect a specific JSON or XML shape.
Bun also releases frequently, and runtime or test-runner behavior can change between versions. Pin the exact Bun version in your lockfile and CI configuration—for example, by installing bun@<version> rather than a floating tag—so that a new release does not silently alter test results.
Before committing to a migration, inspect the current runner capabilities:
bun test --help
Compare the listed flags and matchers against the ones your suite uses most. If you rely heavily on custom Jest transformers, module-level mocking with jest.mock, or coverage thresholds enforced in CI, treat those as blockers until you have verified equivalents.
Actionable takeaway
Do not migrate an entire monorepo in one step. Start by identifying the smallest package or module with the fewest Jest-specific features. Copy one test file, swap the import to bun:test, and run bun test <file> locally. If it passes and the exit code is 0, expand to the full directory with bun test. Remove the old framework from devDependencies only after the entire suite passes and your CI pipeline installs a pinned Bun version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.