bun-types
Version:
Type definitions and documentation for Bun, an incredibly fast JavaScript runtime
343 lines (235 loc) • 8.36 kB
text/mdx
---
title: "Runtime behavior"
description: "Learn about Bun test's runtime integration, environment variables, timeouts, and error handling"
---
`bun test` is deeply integrated with Bun's runtime. This is part of what makes `bun test` fast and simple to use.
## Environment Variables
### NODE_ENV
`bun test` automatically sets `$NODE_ENV` to `"test"` unless it's already set in the environment or via `.env` files. This is standard behavior for most test runners and helps ensure consistent test behavior.
```ts title="test.ts" icon="/icons/typescript.svg"
import { test, expect } from "bun:test";
test("NODE_ENV is set to test", () => {
expect(process.env.NODE_ENV).toBe("test");
});
```
You can override this by setting `NODE_ENV` explicitly:
```bash terminal icon="terminal"
NODE_ENV=development bun test
```
### TZ (Timezone)
By default, all `bun test` runs use UTC (`Etc/UTC`) as the time zone unless overridden by the `TZ` environment variable. This ensures consistent date and time behavior across different development environments.
```ts title="test.ts" icon="/icons/typescript.svg"
import { test, expect } from "bun:test";
test("timezone is UTC by default", () => {
const date = new Date();
expect(date.getTimezoneOffset()).toBe(0);
});
```
To test with a specific timezone:
```bash terminal icon="terminal"
TZ=America/New_York bun test
```
## Test Timeouts
Each test has a default timeout of 5000ms (5 seconds) if not explicitly overridden. Tests that exceed this timeout will fail.
### Global Timeout
Change the timeout globally with the `--timeout` flag:
```bash terminal icon="terminal"
bun test --timeout 10000 # 10 seconds
```
### Per-Test Timeout
Set timeout per test as the third parameter to the test function:
```ts title="test.ts" icon="/icons/typescript.svg"
import { test, expect } from "bun:test";
test("fast test", () => {
expect(1 + 1).toBe(2);
}, 1000); // 1 second timeout
test("slow test", async () => {
await new Promise(resolve => setTimeout(resolve, 8000));
}, 10000); // 10 second timeout
```
### Infinite Timeout
Use `0` or `Infinity` to disable timeout:
```ts title="test.ts" icon="/icons/typescript.svg"
test("test without timeout", async () => {
// This test can run indefinitely
await someVeryLongOperation();
}, 0);
```
## Error Handling
### Unhandled Errors
`bun test` tracks unhandled promise rejections and errors that occur between tests. If such errors occur, the final exit code will be non-zero (specifically, the count of such errors), even if all tests pass.
This helps catch errors in asynchronous code that might otherwise go unnoticed:
```ts title="test.ts" icon="/icons/typescript.svg"
import { test } from "bun:test";
test("test 1", () => {
// This test passes
expect(true).toBe(true);
});
// This error happens outside any test
setTimeout(() => {
throw new Error("Unhandled error");
}, 0);
test("test 2", () => {
// This test also passes
expect(true).toBe(true);
});
// The test run will still fail with a non-zero exit code
// because of the unhandled error
```
### Promise Rejections
Unhandled promise rejections are also caught:
```ts title="test.ts" icon="/icons/typescript.svg"
import { test } from "bun:test";
test("passing test", () => {
expect(1).toBe(1);
});
// This will cause the test run to fail
Promise.reject(new Error("Unhandled rejection"));
```
### Custom Error Handling
You can set up custom error handlers in your test setup:
```ts title="test-setup.ts" icon="/icons/typescript.svg"
process.on("uncaughtException", error => {
console.error("Uncaught Exception:", error);
process.exit(1);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise, "reason:", reason);
process.exit(1);
});
```
## CLI Flags Integration
Several Bun CLI flags can be used with `bun test` to modify its behavior:
### Memory Usage
```bash terminal icon="terminal"
# Reduces memory usage for the test runner VM
bun test --smol
```
### Debugging
```bash terminal icon="terminal"
# Attaches the debugger to the test runner process
bun test --inspect
bun test --inspect-brk
```
### Module Loading
```bash terminal icon="terminal"
# Runs scripts before test files (useful for global setup/mocks)
bun test --preload ./setup.ts
# Sets compile-time constants
bun test --define "process.env.API_URL='http://localhost:3000'"
# Configures custom loaders
bun test --loader .special:special-loader
# Uses a different tsconfig
bun test --tsconfig-override ./test-tsconfig.json
# Sets package.json conditions for module resolution
bun test --conditions development
# Loads environment variables for tests
bun test --env-file .env.test
```
### Installation-related Flags
```bash
# Affect any network requests or auto-installs during test execution
bun test --prefer-offline
bun test --frozen-lockfile
```
## Watch and Hot Reloading
### Watch Mode
When running `bun test` with the `--watch` flag, the test runner will watch for file changes and re-run affected tests.
```bash terminal icon="terminal"
bun test --watch
```
The test runner is smart about which tests to re-run:
```ts title="math.test.ts" icon="/icons/typescript.svg"
import { add } from "./math.js";
import { test, expect } from "bun:test";
test("addition", () => {
expect(add(2, 3)).toBe(5);
});
```
If you modify `math.js`, only `math.test.ts` will re-run, not all tests.
### Hot Reloading
The `--hot` flag provides similar functionality but is more aggressive about trying to preserve state between runs:
```bash terminal icon="terminal"
bun test --hot
```
For most test scenarios, `--watch` is the recommended option as it provides better isolation between test runs.
## Global Variables
The following globals are automatically available in test files without importing (though they can be imported from `bun:test` if preferred):
```ts title="test.ts" icon="/icons/typescript.svg"
// All of these are available globally
test("global test function", () => {
expect(true).toBe(true);
});
describe("global describe", () => {
beforeAll(() => {
// global beforeAll
});
it("global it function", () => {
// it is an alias for test
});
});
// Jest compatibility
jest.fn();
// Vitest compatibility
vi.fn();
```
You can also import them explicitly if you prefer:
```ts title="test.ts" icon="/icons/typescript.svg"
import { test, it, describe, expect, beforeAll, beforeEach, afterAll, afterEach, jest, vi } from "bun:test";
```
## Process Integration
### Exit Codes
`bun test` uses standard exit codes:
- `0`: All tests passed, no unhandled errors
- `1`: Test failures occurred
- `>1`: Number of unhandled errors (even if tests passed)
### Signal Handling
The test runner properly handles common signals:
```bash terminal icon="terminal"
# Gracefully stops test execution
kill -SIGTERM <test-process-pid>
# Immediately stops test execution
kill -SIGKILL <test-process-pid>
```
### Environment Detection
Bun automatically detects certain environments and adjusts behavior:
```ts title="test.ts" icon="/icons/typescript.svg"
// GitHub Actions detection
if (process.env.GITHUB_ACTIONS) {
// Bun automatically emits GitHub Actions annotations
}
// CI detection
if (process.env.CI) {
// Certain behaviors may be adjusted for CI environments
}
```
## Performance Considerations
### Single Process
The test runner runs all tests in a single process by default. This provides:
- **Faster startup** - No need to spawn multiple processes
- **Shared memory** - Efficient resource usage
- **Simple debugging** - All tests in one process
However, this means:
- Tests share global state (use lifecycle hooks to clean up)
- One test crash can affect others
- No true parallelization of individual tests
### Memory Management
```bash terminal icon="terminal"
# Monitor memory usage
bun test --smol # Reduces memory footprint
# For large test suites, consider splitting files
bun test src/unit/
bun test src/integration/
```
### Test Isolation
Since tests run in the same process, ensure proper cleanup:
```ts title="test.ts" icon="/icons/typescript.svg"
import { afterEach } from "bun:test";
afterEach(() => {
// Clean up global state
global.myGlobalVar = undefined;
delete process.env.TEST_VAR;
// Reset modules if needed
jest.resetModules();
});
```