Running TypeScript Unit Tests with Bun's Built-in Test Runner
Learn how to use Bun's built-in test runner to execute TypeScript unit tests without separate transpilation steps, reducing overhead and increasing developer velocity.
08 Feb 2026, 00:18 UTC

The Problem: Transpilation Overhead in Test Suites
Traditional JavaScript testing workflows often require a complex chain of tools to execute TypeScript tests. Developers typically need a test runner (like Jest or Mocha), a transpiler (like ts-jest or esbuild), and a configuration file to bridge the two. This setup increases startup time and introduces version mismatch risks between the transpiler and the runtime.
The takeaway: Bun eliminates this overhead by providing a built-in test runner that executes TypeScript, JSX, and JavaScript natively. You can run tests directly against your source files without a separate build step, significantly reducing the feedback loop during development.
Prerequisites
- Bun Runtime: Installed on your local machine or CI environment. Run
bun --versionto verify the installation. - TypeScript Project: A project containing
.tsor.jsfiles. Note that Bun does not perform type-checking during test execution; it only strips types to run the code. - File Naming: Tests must follow supported naming conventions for automatic discovery, such as
*.test.ts,*.spec.ts,*.test.js, or*.spec.js.
Implementing Your First Test Suite
Bun provides a Jest-compatible API, meaning common globals like expect and test are available without manual imports in most configurations.
1. Create a Test File
Create a file named math.test.ts in your project root or a tests/ directory. In this example, we are testing a simple addition utility.
// math.test.ts
import { expect, test, describe } from "bun:test";
const add = (a: number, b: number) => a + b;
describe("Math Utilities", () => {
test("adds 2 + 3 to equal 5", () => {
expect(add(2, 3)).toBe(5);
});
test("fails when adding incorrect numbers", () => {
// This is used to verify the runner reports failures correctly
expect(add(2, 2)).toBe(5);
});
});
2. Execute the Tests
Run the following command in your terminal from the project root. No permissions beyond standard user access are required for local execution.
bun test
3. Iterative Development with Watch Mode
To avoid manually restarting the suite after every change, use the watch flag. This monitors your file system and re-runs tests when files are saved.
bun test --watch
Diagnostic Checks and Verification
To ensure your testing environment is functioning as expected, perform these three checks:
| Check | Action | Expected Result |
|---|---|---|
| Runner Availability | bun test --help |
A list of available flags and command options. |
| Failure Reporting | Run a test with a known failing assertion. | A non-zero exit code and a detailed diff of expected vs. actual values. |
| TypeScript Handling | Run a .ts file with type annotations. |
Successful execution without requiring tsc or ts-node. |
Comparison: Bun Test vs. Jest
While the API is similar, there are critical engineering differences to consider when deciding whether to migrate or start fresh.
- Startup Speed: Bun starts tests nearly instantaneously because it avoids the heavy startup cost of the Node.js runtime and the transpilation layer.
- Type Checking: Jest (via ts-jest) can be configured to fail tests if there are TypeScript type errors.
bun testignores types entirely; you must runtsc --noEmitseparately in your CI pipeline to catch type mismatches. - Mocking: Bun provides
bun:testmocking utilities, but complex Jest mocks (likejest.mock()for deep module overrides) may behave differently. Always verify custom mock logic after migration.
Limitations and Risks
- Runtime Evolution: Bun is evolving rapidly. A version update may introduce changes to the test runner's behavior. It is recommended to pin your Bun version in
package.jsonor via a.bun-versionfile for CI stability. - Node Compatibility: While Bun supports most
node:built-ins, some niche Node-specific modules may fail. If a test fails unexpectedly, check if the failure is due to a missing runtime implementation rather than a logic error.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.