Core toolchainTest runner

Test runner

Write bun:test suites, isolate dependencies, and run stable CI tests

Last updated on

Minimal test

sum.test.ts
import { describe, expect, test } from 'bun:test';

describe('sum', () => {
  test('adds values', () => {
    expect(2 + 3).toBe(5);
  });
});
bun test
bun test sum
bun test --watch
bun test --coverage

Bun discovers conventional *.test.*, *_test.*, *.spec.*, and *_spec.* files.

Lifecycle and async tests

import { afterEach, beforeEach, expect, test } from 'bun:test';

let controller: AbortController;

beforeEach(() => {
  controller = new AbortController();
});

afterEach(() => {
  controller.abort();
});

test('loads data', async () => {
  const response = await fetch('https://example.com', {
    signal: controller.signal,
  });
  expect(response.ok).toBe(true);
});

Real tests should replace external network calls so they remain repeatable and cannot mutate production data.

Mocking order of preference

  1. Test pure functions directly.
  2. Inject network, clock, random, filesystem, and database boundaries.
  3. Use module mocks only when the boundary cannot reasonably be refactored.
  4. Restore state after every test; avoid order dependence.

Isolation, parallelism, and CI shards

Bun 1.3.13 added file-level isolation and worker parallelism for larger suites:

# Detect state leaking between files locally
bun test --isolate

# Parallel CI workers; --parallel implies --isolate
bun test --parallel --coverage

# Three independent CI jobs
bun test --shard=1/3
bun test --shard=2/3
bun test --shard=3/3

Database integration tests, fixed ports, and shared temporary directories may not be parallel-safe. Allocate a database or schema, port, and temporary directory per worker, or keep that group in a serial job.

Coverage and flaky tests

bunfig.toml
[test]
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "./coverage"
coverageSkipTestFiles = true
coverageThreshold = 0.8

The test process exits non-zero below the threshold. Coverage proves execution, not meaningful assertions; keep explicit scenarios for security, authorization, and migration paths.

bun test --randomize
bun test --seed 12345
bun test --retry 2
bun test --rerun-each 20

Do not hide deterministic bugs behind unbounded retries. Record the failing seed, shard, and Bun version in CI.

Compact output for coding agents

AGENT=1 bun test

Bun suppresses per-test passing noise while retaining failures and the summary. Quiet output does not change the exit status and does not replace complete CI logs.

Migrating from Jest

Bun targets Jest compatibility, but verify custom environments, DOM setup, complex fake timers, Jest-only transformers, native extensions, and snapshot workflows. Start with isolated unit tests, not the most customized suite.

Official references: Test runner, test configuration, and Bun 1.3.13.