Skip to main content
Bun ships with a fast, built-in, Jest-compatible test runner. Tests are executed with the Bun runtime and support the following features:
  • TypeScript and JSX out of the box
  • Lifecycle hooks (beforeAll, beforeEach, afterEach, afterAll)
  • Snapshot testing
  • UI and DOM testing
  • Watch mode with --watch
  • Script preloading with --preload
Bun aims for compatibility with Jest, but not everything is implemented yet. To track progress, see the Jest compatibility tracking issue.

Running tests

bun test
Tests are written in JavaScript or TypeScript with a Jest-like API imported from bun:test.
math.test.ts
import { expect, test } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});

Test discovery

The runner recursively searches the working directory for files matching these patterns:
  • *.test.{js|jsx|ts|tsx}
  • *_test.{js|jsx|ts|tsx}
  • *.spec.{js|jsx|ts|tsx}
  • *_spec.{js|jsx|ts|tsx}
By default, node_modules and hidden directories (starting with .) are excluded.

Run specific files

Pass a path starting with ./ or / to run a specific file:
bun test ./test/auth.test.ts
Pass a substring filter to match files by path:
bun test auth
This matches any file whose path contains auth, such as src/auth/login.test.ts.

Filter by test name

Use -t / --test-name-pattern to filter by test name using a substring or regex pattern:
bun test -t "should handle"
The pattern is matched against the full test title, including all parent describe block labels joined with spaces.

Watch mode

Use --watch to re-run tests automatically when files change:
bun test --watch
Only the affected test files are re-run when a source file changes.

Timeouts

Use --timeout to set a per-test timeout in milliseconds. The default is 5000ms.
bun test --timeout 10000
You can also set a per-test timeout as the third argument to test():
test("slow operation", async () => {
  const result = await heavyComputation();
  expect(result).toBeDefined();
}, 15000);

Bail on failure

Use --bail to stop the test run after a given number of failures. This is useful in CI to avoid spending time on subsequent tests after early failures.
# Stop after the first failure
bun test --bail

# Stop after 5 failures
bun test --bail=5

Concurrent execution

By default, tests run sequentially within each file. Use --concurrent to run async tests in parallel:
bun test --concurrent
Control the maximum concurrency with --max-concurrency (default: 20):
bun test --concurrent --max-concurrency 4
You can also mark individual tests to run concurrently or serially:
import { test, expect } from "bun:test";

// Runs concurrently with other concurrent tests
test.concurrent("fetches user data", async () => {
  const res = await fetch("/api/user/1");
  expect(res.ok).toBe(true);
});

// Always runs sequentially, even with --concurrent
test.serial("updates shared state", () => {
  sharedCounter++;
  expect(sharedCounter).toBeGreaterThan(0);
});

Retrying failed tests

Use --retry to automatically retry failed tests:
bun test --retry 3
Override the retry count per test with the { retry: N } option:
test("flaky network request", async () => {
  const res = await fetch("https://api.example.com");
  expect(res.ok).toBe(true);
}, { retry: 3 });

Randomizing test order

Use --randomize to run tests in a random order, which helps detect hidden dependencies between tests:
bun test --randomize
The seed used for randomization is printed in the summary. Use --seed to reproduce a specific order:
bun test --seed 12345

Reporters

Bun supports multiple output formats for test results.
The default reporter prints human-readable results to the console with color support.
bun test

CI/CD integration

GitHub Actions

Bun automatically detects GitHub Actions and emits annotations. No extra configuration is needed beyond installing Bun:
.github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun test

AI agent integration

When running under an AI coding assistant, set one of these environment variables for quieter output that hides passing tests but preserves failures and the summary:
# Claude Code
CLAUDECODE=1 bun test

# Generic agent flag
AGENT=1 bun test

Lifecycle hooks

Bun supports beforeAll, beforeEach, afterEach, and afterAll hooks for setup and teardown. See Test Lifecycle for full documentation.

Mocks

Create mock functions with mock() from bun:test. See Mocks & Spies for full documentation.
import { test, expect, mock } from "bun:test";

const random = mock(() => Math.random());

test("random", () => {
  const val = random();
  expect(val).toBeGreaterThan(0);
  expect(random).toHaveBeenCalledTimes(1);
});

Snapshot testing

Use .toMatchSnapshot() to save and compare output across test runs. See Snapshots for full documentation.
import { test, expect } from "bun:test";

test("snapshot", () => {
  expect({ a: 1, b: "hello" }).toMatchSnapshot();
});
Update snapshots with:
bun test --update-snapshots

Build docs developers (and LLMs) love