Skip to main content
Bun ships with a fast, built-in, Jest-compatible test runner. Tests run in the Bun runtime and support the following features.
  • TypeScript and JSX
  • Lifecycle hooks
  • Snapshot testing
  • UI & DOM testing
  • Watch mode with --watch
  • Script pre-loading with --preload
Bun aims for compatibility with Jest, but not everything is implemented. To track compatibility, see this tracking issue.

Run tests

terminal
bun test
Tests are written in JavaScript or TypeScript with a Jest-like API. See Writing tests.
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmath.test.ts
import { expect, test } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});
The runner recursively searches the working directory for files that match the following patterns:
  • *.test.{js|jsx|ts|tsx}
  • *_test.{js|jsx|ts|tsx}
  • *.spec.{js|jsx|ts|tsx}
  • *_spec.{js|jsx|ts|tsx}
To filter the set of test files to run, pass additional positional arguments to bun test. Any test file with a path that matches one of the filters runs. Filters are commonly file or directory names; glob patterns are not yet supported.
terminal
bun test <filter> <filter> ...
To filter by test name, use the -t/--test-name-pattern flag.
terminal
# run all tests or test suites with "addition" in the name
bun test --test-name-pattern addition
To run a specific file in the test runner, make sure the path starts with ./ or / to distinguish it from a filter name.
terminal
bun test ./test/specific-file.test.ts
The test runner runs all tests in a single process. It loads all --preload scripts (see Lifecycle), then runs all tests. If a test fails, the test runner exits with a non-zero exit code.

CI/CD integration

bun test supports a variety of CI/CD integrations.

GitHub Actions

bun test automatically detects when it’s running inside GitHub Actions and emits GitHub Actions annotations to the console directly. No configuration is needed, other than installing bun in the workflow and running bun test.

How to install bun in a GitHub Actions workflow

To use bun test in a GitHub Actions workflow, add the following step:
.github/workflows/test.yml
jobs:
  build:
    name: build-app
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install bun
        uses: oven-sh/setup-bun@v2
      - name: Install dependencies # (assuming your project has dependencies)
        run: bun install # You can use npm/yarn/pnpm instead if you prefer
      - name: Run tests
        run: bun test

JUnit XML reports (GitLab, etc.)

To write a JUnit XML report, pass --reporter=junit together with --reporter-outfile.
terminal
bun test --reporter=junit --reporter-outfile=./bun.xml
bun test still writes to stdout/stderr as usual, and writes the JUnit XML report to the given path at the end of the run. JUnit XML is a popular format for reporting test results in CI/CD pipelines.

Timeouts

Use the --timeout flag to specify a per-test timeout in milliseconds. If a test times out, it is marked as failed. The default value is 5000.
terminal
# default value is 5000
bun test --timeout 20

Concurrent test execution

By default, Bun runs all tests sequentially within each test file. Concurrent execution runs async tests in parallel, which speeds up test suites with independent tests.

--concurrent flag

Use the --concurrent flag to run all tests concurrently within their respective files:
terminal
bun test --concurrent
When this flag is enabled, all tests run in parallel unless marked with test.serial.

--max-concurrency flag

Control the maximum number of tests running simultaneously with the --max-concurrency flag:
terminal
# Limit to 4 concurrent tests
bun test --concurrent --max-concurrency 4

# Default: 20
bun test --concurrent
This helps prevent resource exhaustion when running many concurrent tests. The default value is 20.

test.concurrent

Mark individual tests to run concurrently, even when the --concurrent flag is not used:
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmath.test.ts
import { test, expect } from "bun:test";

// These tests run in parallel with each other
test.concurrent("concurrent test 1", async () => {
  await fetch("/api/endpoint1");
  expect(true).toBe(true);
});

test.concurrent("concurrent test 2", async () => {
  await fetch("/api/endpoint2");
  expect(true).toBe(true);
});

// This test runs sequentially
test("sequential test", () => {
  expect(1 + 1).toBe(2);
});

test.serial

Force tests to run sequentially, even when the --concurrent flag is enabled:
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmath.test.ts
import { test, expect } from "bun:test";

let sharedState = 0;

// These tests must run in order
test.serial("first serial test", () => {
  sharedState = 1;
  expect(sharedState).toBe(1);
});

test.serial("second serial test", () => {
  // Depends on the previous test
  expect(sharedState).toBe(1);
  sharedState = 2;
});

// This test can run concurrently if --concurrent is enabled
test("independent test", () => {
  expect(true).toBe(true);
});

// Chaining test qualifiers
test.failing.each([1, 2, 3])("chained qualifiers %d", input => {
  expect(input).toBe(0); // This test is expected to fail for each input
});

Retry failed tests

Use the --retry flag to automatically retry failed tests up to a given number of times. If a test fails and then passes on a subsequent attempt, it is reported as passing.
terminal
bun test --retry 3
Per-test { retry: N } overrides the global --retry value:
// Uses the global --retry value
test("uses global retry", () => {
  /* ... */
});

// Overrides --retry with its own value
test("custom retry", { retry: 1 }, () => {
  /* ... */
});
You can also set this in bunfig.toml:
bunfig.toml
[test]
retry = 3

Rerun tests

Use the --rerun-each flag to run each test multiple times. This surfaces flaky or non-deterministic test failures.
terminal
bun test --rerun-each 100

Randomize test execution order

Use the --randomize flag to run tests in a random order. This helps detect tests that depend on shared state or execution order.
terminal
bun test --randomize
With --randomize, the seed used for randomization is displayed in the test summary:
terminal
bun test --randomize
# ... test output ...
 --seed=12345
 2 pass
 8 fail
Ran 10 tests across 2 files. [50.00ms]

Reproducible random order with --seed

Use the --seed flag to specify the randomization seed and reproduce the same test order when debugging order-dependent failures.
terminal
# Reproduce a previous randomized run
bun test --seed 123456
The --seed flag implies --randomize, so you don’t need to specify both. The same seed always produces the same test execution order.

Bail out with --bail

Use the --bail flag to abort the test run after a given number of test failures. By default, Bun runs all tests and reports all failures, but in CI it can be preferable to stop early and reduce CPU usage.
terminal
# bail after 1 failure
bun test --bail

# bail after 10 failure
bun test --bail=10

Watch mode

Like bun run, bun test accepts the --watch flag to watch for changes and re-run tests.
terminal
bun test --watch

Lifecycle hooks

Bun supports the following lifecycle hooks:
HookDescription
beforeAllRuns once before all tests.
beforeEachRuns before each test.
afterEachRuns after each test.
afterAllRuns once after all tests.
Define hooks inside test files, or in a separate file preloaded with the --preload flag.
terminal
bun test --preload ./setup.ts
See Lifecycle.

Mocks

Create mock functions with the mock function.
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmath.test.ts
import { test, expect, mock } from "bun:test";
const random = mock(() => Math.random());

test("random", () => {
  const val = random();
  expect(val).toBeGreaterThan(0);
  expect(random).toHaveBeenCalled();
  expect(random).toHaveBeenCalledTimes(1);
});
Alternatively, use jest.fn(); it behaves identically.
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmath.test.ts
import { test, expect, mock } from "bun:test"; 
import { test, expect, jest } from "bun:test"; 

const random = mock(() => Math.random()); 
const random = jest.fn(() => Math.random()); 
See Mocks.

Snapshot testing

bun test supports snapshot testing.
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmath.test.ts
// example usage of toMatchSnapshot
import { test, expect } from "bun:test";

test("snapshot", () => {
  expect({ a: 1 }).toMatchSnapshot();
});
To update snapshots, use the --update-snapshots flag.
terminal
bun test --update-snapshots
See Snapshots.

UI & DOM testing

Bun is compatible with popular UI testing libraries: See DOM testing.

Performance

Bun’s test runner is fast.
Running 266 React SSR tests faster than Jest can print its version number.

AI Agent Integration

When you use Bun’s test runner with an AI coding assistant, you can enable quieter output that keeps failure details but drops the rest of the noise.

Environment Variables

Set any of the following environment variables to enable AI-friendly output:
  • CLAUDECODE=1 - For Claude Code
  • REPL_ID=1 - For Replit
  • AGENT=1 - Generic AI agent flag

Behavior

When an AI agent environment is detected:
  • Only test failures are displayed in detail
  • Passing, skipped, and todo test indicators are hidden
  • Summary statistics remain intact
terminal
# Example: Enable quiet output for Claude Code
CLAUDECODE=1 bun test

# Still shows failures and summary, but hides verbose passing test output

CLI Usage

bun test <patterns>

Execution Control

--timeout
number
default:"5000"
Set the per-test timeout in milliseconds (default 5000)
--rerun-each
number
Re-run each test file NUMBER times to help catch certain bugs
--retry
number
Retry failed tests up to NUMBER times. Overridden by per-test
--concurrent
boolean
Treat all tests as test.concurrent() tests
--randomize
boolean
Run tests in random order
--seed
number
Set the random seed for test randomization
--bail
number
default:"1"
Exit the test suite after NUMBER failures. If you do not specify a number, it defaults to 1.
--max-concurrency
number
default:"20"
Maximum number of concurrent tests to execute at once (default 20)

Test Filtering

--todo
boolean
Include tests that are marked with test.todo()
--test-name-pattern
string
Run only tests with a name that matches the given regex. Alias: -t

Reporting

--reporter
string
Test output reporter format. Available: junit (requires —reporter-outfile), dots. Default: console output.
--reporter-outfile
string
Output file path for the reporter format (required with —reporter)
--dots
boolean
Enable dots reporter. Shorthand for —reporter=dots

Coverage

--coverage
boolean
Generate a coverage profile
--coverage-reporter
string
default:"text"
Report coverage in text and/or lcov. Defaults to text
--coverage-dir
string
default:"coverage"
Directory for coverage files. Defaults to coverage

Snapshots

--update-snapshots
boolean
Update snapshot files. Alias: -u

Examples

Run all test files:
terminal
bun test
Run all test files with “foo” or “bar” in the file name:
terminal
bun test foo bar
Run all test files, only including tests whose name includes “baz”:
terminal
bun test --test-name-pattern baz