Skip to main content
Bun’s test runner supports hot reloading with the --watch flag. Tests automatically re-run when source files or test files change.

Basic usage

Enable watch mode:
$ bun test --watch
Bun watches for file changes and re-runs affected tests automatically.

How it works

When a file changes, Bun:
  1. Identifies which test files import the changed file
  2. Re-runs only the affected tests
  3. Displays results immediately
This makes the feedback loop extremely fast.

Watch mode features

Instant feedback

See test results as soon as you save:
$ bun test --watch
Watching for changes...

[1:23:45 PM] File changed: src/utils.ts
 utils.test.ts (3 tests) 12ms

Smart test selection

Only affected tests run:
project/
├── src/
│   ├── math.ts          <- Changed file
│   └── utils.ts
└── test/
    ├── math.test.ts     <- Runs (imports math.ts)
    └── utils.test.ts    <- Skipped (doesn't import math.ts)

Watch all tests

Re-run all tests on any change:
$ bun test --watch
Press a in watch mode to toggle between affected tests and all tests.

Watch mode commands

Interactive commands in watch mode:
  • Enter - Re-run tests
  • a - Run all tests
  • f - Run only failed tests
  • t - Filter by test name pattern
  • q - Quit watch mode

Filtering in watch mode

Filter by test name

Press t and type a pattern:
Filter by test name: auth
Only tests matching “auth” will run.

Run only failed tests

Press f to run only tests that failed:
Run only failed tests: ON

Watch with other flags

Combine watch mode with other test flags:
# Watch mode with coverage
$ bun test --watch --coverage

# Watch mode with specific reporter
$ bun test --watch --reporter dot

# Watch mode with timeout
$ bun test --watch --timeout 10000

Ignoring files

Configure which files to watch in bunfig.toml:
[test]
watchIgnore = [
  "node_modules/**",
  "dist/**",
  "coverage/**"
]

Watch specific files

Watch specific test files:
$ bun test --watch src/utils.test.ts
Watch tests matching a pattern:
$ bun test --watch --test-name-pattern "integration"

Performance tips

Optimize test setup

Minimize expensive setup in test files:
import { test, expect, beforeAll } from "bun:test";

// ❌ Expensive global setup runs on every reload
const db = await connectToDatabase();

// ✅ Better: Setup once in beforeAll
let db;
beforeAll(async () => {
  db = await connectToDatabase();
});

Use lazy imports

Import heavy dependencies lazily:
import { test, expect } from "bun:test";

test("large library", async () => {
  const { heavy } = await import("./heavy-lib.ts");
  expect(heavy()).toBe(true);
});

Exclude slow tests

Skip slow tests in watch mode:
import { test, expect } from "bun:test";

test.skipIf(process.env.WATCH_MODE)("slow integration test", async () => {
  // This test is skipped in watch mode
  await slowIntegrationTest();
});
Run with:
$ WATCH_MODE=1 bun test --watch

Development workflow

Typical TDD workflow with watch mode:
  1. Start watch mode:
    $ bun test --watch
    
  2. Write a failing test
  3. Save the file - test runs automatically and fails
  4. Write implementation code
  5. Save - test runs automatically and passes
  6. Refactor with confidence

CI vs Development

Watch mode is for development only. In CI:
# Development
$ bun test --watch

# CI
$ bun test

Troubleshooting

Tests not re-running

Ensure files are being watched:
# Check if files are in ignored patterns
$ cat bunfig.toml

Too many files being watched

Increase file watcher limit on Linux:
$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
$ sudo sysctl -p

Watch mode is slow

  • Reduce the number of test files
  • Optimize beforeAll/beforeEach hooks
  • Use test.concurrent() for parallel execution

Build docs developers (and LLMs) love