Bun automatically discovers test files based on naming conventions and directory structure.
Default discovery patterns
Bun recognizes test files with these patterns:
File extensions
*.test.ts
*.test.tsx
*.test.js
*.test.jsx
*.test.mjs
*.test.cjs
And with underscore:
*_test.ts
*_test.tsx
*_test.js
*_test.jsx
*_test.mjs
*_test.cjs
Test directories
Files in __tests__ directories:
project/
├── src/
│ ├── utils.ts
│ └── __tests__/
│ └── utils.ts <- Discovered as test
└── components/
├── Button.tsx
└── __tests__/
└── Button.test.tsx <- Discovered as test
Running all tests
Run all discovered tests:
Bun searches the current directory and subdirectories for test files.
Running specific tests
Specific file
$ bun test path/to/file.test.ts
Multiple files
$ bun test file1.test.ts file2.test.ts
Directory
$ bun test src/components/
Pattern matching
# Run all test files containing "auth"
$ bun test auth
# Fuzzy match
$ bun test compo/butt
# Matches: components/Button.test.tsx
Test file structure
Recommended structure
Colocate tests with source code:
project/
├── src/
│ ├── math.ts
│ ├── math.test.ts
│ ├── utils.ts
│ └── utils.test.ts
└── package.json
Or separate test directory:
project/
├── src/
│ ├── math.ts
│ └── utils.ts
├── test/
│ ├── math.test.ts
│ └── utils.test.ts
└── package.json
Configuration
Configure test discovery in bunfig.toml:
Root directory
Set the root directory for test discovery:
Bun will only search for tests in ./src and its subdirectories.
Custom patterns
Configure custom test file patterns:
[test]
patterns = [
"**/*.test.ts",
"**/*.spec.ts",
"**/__tests__/**/*.ts"
]
Exclude patterns
Exclude directories from test discovery:
[test]
exclude = [
"**/node_modules/**",
"**/dist/**",
"**/*.d.ts"
]
Import resolution
Bun resolves imports in test files using the same rules as regular code:
Relative imports
import { add } from "./math";
import { expect, test } from "bun:test";
test("add", () => {
expect(add(1, 2)).toBe(3);
});
Package imports
import { test, expect } from "bun:test";
import { format } from "date-fns";
test("date formatting", () => {
expect(format(new Date("2024-01-01"), "yyyy-MM-dd")).toBe("2024-01-01");
});
Path mapping
Use tsconfig.json path mapping:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
import { add } from "@/math";
import { test, expect } from "bun:test";
test("add", () => {
expect(add(1, 2)).toBe(3);
});
Preloading modules
Load modules before running tests:
[test]
preload = [
"./test/setup.ts",
"./test/global-mocks.ts"
]
Preloaded files run before any test files are loaded. Use this for:
- Setting up global test utilities
- Mocking built-in modules
- Configuring test environment
import { beforeAll } from "bun:test";
beforeAll(() => {
// Global setup
process.env.NODE_ENV = "test";
});
Test execution order
Bun runs tests in this order:
- Load preload modules (from
bunfig.toml)
- Discover test files
- Load each test file
- Execute tests in each file:
beforeAll hooks
- For each test:
beforeEach hooks
- Test function
afterEach hooks
afterAll hooks
Parallel execution
Bun can run test files in parallel:
Or configure in bunfig.toml:
Tests within the same file always run sequentially. Only different test files run in parallel.
Filtering tests
By name pattern
$ bun test --test-name-pattern "user authentication"
Only tests whose names match the pattern will run.
By file pattern
$ bun test "src/**/*.integration.test.ts"
By label
Coming soon: Filter tests by custom labels.
Debugging test discovery
See which tests Bun discovers:
This lists all test files without running them.
Reduce test discovery time
Limit search scope:
[test]
root = "./src"
exclude = ["**/node_modules/**", "**/dist/**"]
Optimize test file size
Split large test files:
❌ utils.test.ts (1000 tests)
✅ utils/
├── string.test.ts (200 tests)
├── array.test.ts (200 tests)
├── object.test.ts (200 tests)
├── number.test.ts (200 tests)
└── date.test.ts (200 tests)
Use concurrent tests
Mark tests as concurrent when possible:
import { test } from "bun:test";
test.concurrent("test 1", async () => {
// ...
});
test.concurrent("test 2", async () => {
// ...
});