Test configuration

Configure bun test behavior with bunfig.toml and command-line options

Configure bun test with bunfig.toml and command-line options.

Configuration File#

To configure bun test in bunfig.toml, add a [test] section:

bunfig.toml
[test]
# Options go here

Test Discovery#

root#

The root option sets the directory Bun scans for tests, instead of the project root.

bunfig.toml
[test]
root = "src"  # Only scan for tests in the src directory

Examples#

bunfig.toml
[test]
# Only run tests in the src directory
root = "src"

# Run tests in a specific test directory
root = "tests"

# Run tests in multiple specific directories (not currently supported - use patterns instead)
# root = ["src", "lib"]  # This syntax is not supported

Preload Scripts#

The preload option loads scripts before the tests run:

bunfig.toml
[test]
preload = ["./test-setup.ts", "./global-mocks.ts"]

This is equivalent to using --preload on the command line:

terminal
bun test --preload ./test-setup.ts --preload ./global-mocks.ts

Common Preload Use Cases#

test-setup.ts
// Global test setup
import { beforeAll, afterAll } from "bun:test";

beforeAll(() => {
  // Set up test database
  setupTestDatabase();
});

afterAll(() => {
  // Clean up
  cleanupTestDatabase();
});
global-mocks.ts
// Global mocks
import { mock } from "bun:test";

// Mock environment variables
process.env.NODE_ENV = "test";
process.env.API_URL = "http://localhost:3001";

// Mock external dependencies
mock.module("./external-api", () => ({
  fetchData: mock(() => Promise.resolve({ data: "test" })),
}));

Environment#

environment sets the DOM that test files run with. It can be "happy-dom", "jsdom", or "node" for no DOM (the default). The package has to be installed in your project. See DOM testing.

bunfig.toml
[test]
environment = "happy-dom"

The --environment flag does the same thing and overrides bunfig.toml:

terminal
bun test --environment=happy-dom

A // @vitest-environment <name> comment in a test file overrides both. So does a /** @jest-environment <name> */ comment at the very top of the file.

Globals#

By default, test, expect and the other globals come from "bun:test". A file that imports anything from "vitest" gets them from "vitest" instead. If that file is a test file or a preload script, so do the modules it loads.

Set globals = "vitest" to get them from "vitest" in every file, like globals: true in a Vitest project. See what differs.

bunfig.toml
[test]
globals = "vitest" # default "bun"

The --globals flag overrides bunfig.toml. Write it with =, as in --globals=vitest.

  • With globals = "vitest", the names are also properties of globalThis, as in Vitest. Some packages look for globalThis.afterEach there.
  • Imports from "bun:test" and "@jest/globals" keep those modules' behavior.
  • true and false, the values Vitest takes, are accepted and change nothing. So is --globals without a value.
  • If a preload script sets one of these globals itself (globalThis.expect = chai.expect), files that import from "bun:test", "@jest/globals" or "vitest" keep the preload's value.
  • As in Vitest, the "vitest" globals don't include jest, xit, xtest or xdescribe.
  • Vitest also defines assert and chai as globals. Bun doesn't, so import assert from "vitest".
  • For their types, see bun-types/vitest-globals.
  • A module keeps the globals it was first loaded with. With --isolate, it keeps them until the next file. So a library loaded by a preload script sees jest, unless that script imports from "vitest" or globals = "vitest" is set.

Libraries such as @testing-library/dom look for a global jest and use it to advance fake timers while they wait. Without one, they leave the clock alone. To make them advance it, use vi.useFakeTimers({ shouldAdvanceTime: true }) or set globalThis.jest = { advanceTimersByTime: ms => vi.advanceTimersByTime(ms) }.

React Compiler#

bun test can compile your components and hooks with Bun's built-in React Compiler, so that your tests run what your build ships. The compiled code imports react/compiler-runtime, which React 19 has. This option is experimental.

bunfig.toml
[test]
reactCompiler = true # default false

Bun compiles every JavaScript and TypeScript file except test files, preload scripts and node_modules. It leaves alone a file or a function that has the "use no memo" directive, and a function that breaks the Rules of React.

To compile part of a project, list folders or glob patterns. They are relative to the directory you run bun test in.

bunfig.toml
[test]
reactCompiler = ["src", "packages/ui"]

Use a table to leave out some of what's in them:

bunfig.toml
[test.reactCompiler]
include = ["src", "packages/ui"]
exclude = ["**/*.stories.*", "**/__mocks__/**"]
  • A folder includes everything inside it. "." is the directory you run bun test in, and "../../packages/ui" is a folder outside of it.
  • A glob pattern is matched against the file's path relative to that directory. ** also matches .., so **/*.stories.* applies to the folders outside of it too.
  • A workspace package that is linked into node_modules is compiled. List it by its real path, like "../../packages/ui".

The --react-compiler flag turns the compiler on for one run. It compiles what bunfig.toml includes, or every file if bunfig.toml doesn't say.

  • What importOriginal() and vi.importActual() load is compiled like any other import of that file.
  • A module that isn't a file, like a data: URL, isn't compiled.
  • To check that a function was compiled, look for its cache in the source: /\$\d*\[\d+\]/.test(String(Component)).

Path Ignore Patterns#

pathIgnorePatterns excludes files and directories from test discovery entirely, using glob patterns. Unlike coveragePathIgnorePatterns, which only affects coverage reports, pathIgnorePatterns prevents Bun from discovering matching paths and running them as tests.

Use it when your project contains submodules, vendored code, or other directories with *.test.ts files that you don't want bun test to pick up.

bunfig.toml
[test]
# Single pattern
pathIgnorePatterns = "vendor/**"

# Multiple patterns
pathIgnorePatterns = [
  "vendor/**",
  "submodules/**",
  "fixtures/**"
]

This is equivalent to using --path-ignore-patterns on the command line:

terminal
bun test --path-ignore-patterns 'vendor/**' --path-ignore-patterns 'fixtures/**'

Bun prunes directories matching a pattern during scanning and never traverses their contents, so ignoring a large directory tree is cheap.

Common Use Cases#

bunfig.toml
[test]
pathIgnorePatterns = [
  # Git submodules with their own test suites
  "submodules/**",

  # Vendored dependencies
  "vendor/**",
  "third-party/**",

  # Test fixtures that look like tests but aren't
  "fixtures/**",
  "**/test-data/**",

  # Integration / E2E tests you want to run separately
  "**/integration/**",
  "e2e/**"
]

Command-line --path-ignore-patterns flags override the bunfig.toml value entirely. Bun does not merge the two.

Reporters#

JUnit Reporter#

Configure the JUnit reporter output file path directly in the config file:

bunfig.toml
[test.reporter]
junit = "path/to/junit.xml"  # Output path for JUnit XML report

This complements the --reporter=junit and --reporter-outfile CLI flags:

terminal
# Equivalent command line usage
bun test --reporter=junit --reporter-outfile=./junit.xml

Multiple Reporters#

You can use multiple reporters simultaneously:

terminal
# CLI approach
bun test --reporter=junit --reporter-outfile=./junit.xml

# Config file approach
bunfig.toml
[test.reporter]
junit = "./reports/junit.xml"

[test]
# Also enable coverage reporting
coverage = true
coverageReporter = ["text", "lcov"]

Memory Usage#

smol Mode#

Enable the --smol memory-saving mode specifically for the test runner:

bunfig.toml
[test]
smol = true  # Reduce memory usage during test runs

This is equivalent to using the --smol flag on the command line:

terminal
bun test --smol

The smol mode reduces memory usage by:

  • Using less memory for the JavaScript heap
  • Being more aggressive about garbage collection
  • Reducing buffer sizes where possible

Use it in memory-constrained environments, such as CI runners, or for large test suites.

Test execution#

concurrentTestGlob#

Run test files matching a glob pattern with concurrent test execution enabled.

bunfig.toml
[test]
concurrentTestGlob = "**/concurrent-*.test.ts"  # Run files matching this pattern concurrently

Test files matching the pattern behave as if you passed the --concurrent flag: every test in those files runs concurrently. Use this to migrate a test suite to concurrent execution gradually, or to run one kind of test (say, integration tests) concurrently while the rest stay sequential.

The --concurrent CLI flag overrides this setting, forcing all tests to run concurrently regardless of the glob pattern.

randomize#

Run tests in random order to identify tests with hidden dependencies:

bunfig.toml
[test]
randomize = true

seed#

Specify a seed for reproducible random test order. Requires randomize = true:

bunfig.toml
[test]
randomize = true
seed = 2444615283

retry#

Default retry count for all tests. Bun retries a failed test up to this many times. Per-test { retry: N } overrides this value. Default 0 (no retries).

bunfig.toml
[test]
retry = 3

The --retry CLI flag overrides this setting.

rerunEach#

Re-run each test file multiple times to identify flaky tests:

bunfig.toml
[test]
rerunEach = 3

clearMocks, mockReset, restoreMocks, unstubEnvs, unstubGlobals#

Clean up the previous test's mocks and stubs. The cleanup runs before each test and each retry, ahead of the beforeEach hooks. The option names and the order they run in are the same as Vitest's:

bunfig.toml
[test]
restoreMocks = true   # mock.restore() / vi.restoreAllMocks()
mockReset = true      # vi.resetAllMocks()
clearMocks = true     # mock.clearAllMocks()
unstubEnvs = true     # vi.unstubAllEnvs()
unstubGlobals = true  # vi.unstubAllGlobals()

They're all off by default, except that clearMocks is on for tests that use test from "vitest", like in Vitest 5. Set clearMocks = false to turn that off. vi.setConfig({ clearMocks: true }) changes an option for the rest of the test file.

Coverage Options#

Basic Coverage Settings#

bunfig.toml
[test]
# Enable coverage by default
coverage = true

# Set coverage reporter
coverageReporter = ["text", "lcov"]

# Set coverage output directory
coverageDir = "./coverage"

Skip Test Files from Coverage#

Exclude files matching test patterns (for example *.test.ts) from the coverage report:

bunfig.toml
[test]
coverageSkipTestFiles = true  # Exclude test files from coverage reports

Coverage Thresholds#

Specify the coverage threshold as a single number or as an object with per-metric thresholds:

bunfig.toml
[test]
# Simple threshold - applies to lines and functions
coverageThreshold = 0.8

# Detailed thresholds
coverageThreshold = { lines = 0.9, functions = 0.8 }

Setting a threshold makes bun test exit with code 1 when coverage is enabled and any file's line or function coverage is below it. Bun accepts the statements key but does not currently enforce it. The check applies with any coverage reporter.

Threshold Examples#

bunfig.toml
[test]
# Require 90% coverage across the board
coverageThreshold = 0.9

# Different requirements for different metrics
coverageThreshold = {
  lines = 0.85,      # 85% line coverage
  functions = 0.90   # 90% function coverage
}

Coverage Path Ignore Patterns#

Exclude specific files or file patterns from coverage reports using glob patterns:

bunfig.toml
[test]
# Single pattern
coveragePathIgnorePatterns = "**/*.spec.ts"

# Multiple patterns
coveragePathIgnorePatterns = [
  "**/*.spec.ts",
  "**/*.test.ts",
  "src/utils/**",
  "*.config.js",
  "generated/**",
  "vendor/**"
]

Bun excludes files matching any of these patterns from coverage calculation and reporting. See Code coverage.

Common Ignore Patterns#

bunfig.toml
[test]
coveragePathIgnorePatterns = [
  # Test files
  "**/*.test.ts",
  "**/*.spec.ts",
  "**/*.e2e.ts",

  # Configuration files
  "*.config.js",
  "*.config.ts",
  "webpack.config.*",
  "vite.config.*",

  # Build output
  "dist/**",
  "build/**",
  ".next/**",

  # Generated code
  "generated/**",
  "**/*.generated.ts",

  # Vendor/third-party
  "vendor/**",
  "third-party/**",

  # Utilities that don't need testing
  "src/utils/constants.ts",
  "src/types/**"
]

Sourcemap Handling#

Bun transpiles every file, so coverage results pass through sourcemaps before they're reported. coverageIgnoreSourcemaps opts out of this, but the results are confusing: during transpilation, Bun may move code around and rename variables. The option is mostly useful for debugging coverage issues.

bunfig.toml
[test]
coverageIgnoreSourcemaps = true  # Don't use sourcemaps for coverage analysis

When using this option, you probably want to stick a // @bun comment at the top of the source file to opt out of the transpilation process.

Install Settings Inheritance#

bun test inherits network and installation configuration (such as registry, cafile, prefer, and exact) from the [install] section of bunfig.toml. This matters if your tests reach a private registry or trigger installs during the run.

bunfig.toml
[install]
# These settings are inherited by bun test
registry = "https://npm.company.com/"
exact = true
prefer = "offline"

[test]
# Test-specific configuration
coverage = true

Environment Variables#

Set environment variables for tests with .env files, which Bun loads from your project root automatically. For test-specific variables, create a .env.test file, which bun test loads automatically:

.env.test
NODE_ENV=test
DATABASE_URL=postgresql://localhost:5432/test_db
LOG_LEVEL=error

Complete Configuration Example#

An example showing the available test configuration options:

bunfig.toml
[install]
# Install settings inherited by tests
registry = "https://registry.npmjs.org/"
exact = true

[test]
# Test discovery
root = "src"
preload = ["./test-setup.ts", "./global-mocks.ts"]
pathIgnorePatterns = ["vendor/**", "submodules/**"]

# Execution settings
smol = true

# Coverage configuration
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "./coverage"
coverageThreshold = { lines = 0.85, functions = 0.90 }
coverageSkipTestFiles = true
coveragePathIgnorePatterns = [
  "**/*.spec.ts",
  "src/utils/**",
  "*.config.js",
  "generated/**"
]

# Advanced coverage settings
coverageIgnoreSourcemaps = false

# Reporter configuration
[test.reporter]
junit = "./reports/junit.xml"

CLI Override Behavior#

Command-line options always override configuration file settings:

bunfig.toml
[test]
coverage = false
terminal
# This CLI flag overrides the config file
bun test --coverage
# coverage will be enabled