UNPKG

bun-types

Version:

Type definitions and documentation for Bun, an incredibly fast JavaScript runtime

343 lines (235 loc) • 8.36 kB
--- 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(); }); ```