Skip to main content
Define tests with a Jest-like API imported from the built-in bun:test module. Long term, Bun aims for complete Jest compatibility; a limited set of expect matchers is supported.

Basic Usage

To define a test:
math.test.ts

Grouping Tests

Group tests into suites with describe.
math.test.ts

Async Tests

Tests can be async.
math.test.ts
Alternatively, use the done callback to signal completion. If your test function takes a done parameter, you must call it or the test hangs.
math.test.ts

Timeouts

Optionally specify a per-test timeout in milliseconds by passing a number as the third argument to test.
math.test.ts
In bun:test, a timeout throws an uncatchable exception to force the test to stop running and fail. Bun also kills any child processes spawned in the test, so they don’t linger as zombie processes. The default timeout for each test is 5000ms (5 seconds) if not overridden by this timeout option or jest.setTimeout().

Retries and Repeats

test.retry

Use the retry option to automatically retry a flaky test when it fails. The test passes if it succeeds within the specified number of attempts.
example.test.ts

test.repeats

Use the repeats option to run a test multiple times regardless of pass/fail status; the test fails if any iteration fails. Use it to detect flaky tests or for stress testing. repeats: N runs the test N+1 times total (1 initial run + N repeats).
example.test.ts
You cannot use both retry and repeats on the same test.

🧟 Zombie Process Killer

When a test times out, Bun kills any processes spawned in it with Bun.spawn, Bun.spawnSync, or node:child_process that are still running, and logs a message to the console. This prevents zombie processes from lingering after timed-out tests.

Test Modifiers

test.skip

Skip individual tests with test.skip. These tests are not run.
math.test.ts

test.todo

Mark a test as a todo with test.todo. These tests are not run.
math.test.ts
To run todo tests and find any that pass, use bun test --todo.
terminal
With this flag, failing todo tests do not cause an error, but todo tests that pass are marked as failing so you can remove the todo mark or fix the test.

test.only

To run a particular test or suite of tests, use test.only() or describe.only().
example.test.ts
The following command runs only tests #2 and #3.
terminal

test.if

To run a test conditionally, use test.if(). The test runs if the condition is truthy. Use it for tests that should only run on a specific architecture or operating system.
example.test.ts

test.skipIf

To instead skip a test based on some condition, use test.skipIf() or describe.skipIf().
example.test.ts

test.todoIf

To mark the test as TODO instead, use test.todoIf() or describe.todoIf(). The choice between skipIf and todoIf signals intent: “invalid for this target” versus “planned but not implemented yet.”
example.test.ts

test.failing

Use test.failing() when you know a test is failing but you want to track it and be notified when it starts passing. This inverts the test result:
  • A failing test marked with .failing() passes
  • A passing test marked with .failing() fails, with a message that it now passes and should be fixed
math.test.ts
Use it to track known bugs you plan to fix later, or for test-driven development.

Conditional Tests for Describe Blocks

The conditional modifiers .if(), .skipIf(), and .todoIf() also work on describe blocks, affecting all tests in the suite:
example.test.ts

Parametrized Tests

test.each and describe.each

To run the same test with multiple sets of data, use test.each. This creates a parametrized test that runs once for each test case provided.
math.test.ts
describe.each creates a parametrized suite that runs once for each test case:
sum.test.ts

Argument Passing

How arguments are passed to your test function depends on the structure of your test cases:
  • If a table row is an array (like [1, 2, 3]), each element is passed as an individual argument
  • If a row is not an array (like an object), it’s passed as a single argument
example.test.ts

Format Specifiers

Use these specifiers to format the test title:

Examples

example.test.ts

Assertion Counting

Bun supports verifying that a specific number of assertions were called during a test:

expect.hasAssertions()

Use expect.hasAssertions() to verify that at least one assertion is called during a test:
example.test.ts
This is especially useful in async tests, to make sure your assertions run.

expect.assertions(count)

Use expect.assertions(count) to verify that a specific number of assertions are called during a test:
example.test.ts
This helps ensure all your assertions run, especially in complex async code with multiple code paths.

Type Testing

Bun includes expectTypeOf for testing TypeScript types, compatible with Vitest.

expectTypeOf

These functions are no-ops at runtime. Run TypeScript separately to verify the type checks.
The expectTypeOf function provides type-level assertions that are checked by TypeScript’s type checker. To test your types:
  1. Write your type assertions using expectTypeOf
  2. Run bunx tsc --noEmit to check that your types are correct
example.test.ts
For full documentation on expectTypeOf matchers, see the API Reference.

Matchers

Bun implements the following matchers. Full Jest compatibility is planned; see the tracking issue.

Basic Matchers

String and Array Matchers

Object Matchers

Number Matchers

Function and Class Matchers

Promise Matchers

Mock Function Matchers

Snapshot Matchers

Utility Matchers

Not Yet Implemented

Best Practices

Use Descriptive Test Names

example.test.ts
auth.test.ts

Use Appropriate Matchers

auth.test.ts

Test Error Conditions

example.test.ts

Use Setup and Teardown

example.test.ts