Documentation Index
Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
Use this file to discover all available pages before exploring further.
Bun’s test runner provides lifecycle hooks to set up and tear down test state. These hooks are compatible with Jest.
Available hooks
beforeAll
Runs once before all tests in a describe block:
import { describe, test, beforeAll, expect } from "bun:test";
describe("database tests", () => {
let db;
beforeAll(async () => {
// Runs once before all tests
db = await connectToDatabase();
});
test("query 1", () => {
expect(db.query("SELECT 1")).toBe(1);
});
test("query 2", () => {
expect(db.query("SELECT 2")).toBe(2);
});
});
beforeEach
Runs before each test:
import { describe, test, beforeEach, expect } from "bun:test";
describe("counter tests", () => {
let counter;
beforeEach(() => {
// Runs before each test
counter = 0;
});
test("increment", () => {
counter++;
expect(counter).toBe(1);
});
test("increment twice", () => {
counter++;
counter++;
expect(counter).toBe(2);
});
});
afterEach
Runs after each test:
import { describe, test, afterEach } from "bun:test";
describe("cleanup tests", () => {
afterEach(() => {
// Clean up after each test
cleanupTempFiles();
});
test("creates temp file", () => {
createTempFile("test1.txt");
});
test("creates another temp file", () => {
createTempFile("test2.txt");
});
});
afterAll
Runs once after all tests in a describe block:
import { describe, test, beforeAll, afterAll } from "bun:test";
describe("server tests", () => {
let server;
beforeAll(async () => {
server = await startServer();
});
afterAll(async () => {
// Runs once after all tests
await server.stop();
});
test("responds to requests", async () => {
const response = await fetch(server.url);
expect(response.status).toBe(200);
});
});
Async hooks
All hooks support async operations:
import { beforeEach, afterEach, test } from "bun:test";
beforeEach(async () => {
await database.connect();
await database.seed();
});
afterEach(async () => {
await database.clear();
await database.disconnect();
});
test("database query", async () => {
const result = await database.query("SELECT * FROM users");
expect(result).toHaveLength(5);
});
Hook execution order
Hooks execute in the following order:
beforeAll (outer describe)
beforeAll (inner describe)
beforeEach (outer describe)
beforeEach (inner describe)
- test
afterEach (inner describe)
afterEach (outer describe)
afterAll (inner describe)
afterAll (outer describe)
Example:
import { describe, test, beforeAll, beforeEach, afterEach, afterAll } from "bun:test";
describe("outer", () => {
beforeAll(() => console.log("outer beforeAll"));
beforeEach(() => console.log("outer beforeEach"));
afterEach(() => console.log("outer afterEach"));
afterAll(() => console.log("outer afterAll"));
test("outer test", () => {
console.log("outer test");
});
describe("inner", () => {
beforeAll(() => console.log("inner beforeAll"));
beforeEach(() => console.log("inner beforeEach"));
afterEach(() => console.log("inner afterEach"));
afterAll(() => console.log("inner afterAll"));
test("inner test", () => {
console.log("inner test");
});
});
});
// Output:
// outer beforeAll
// outer beforeEach
// outer test
// outer afterEach
// inner beforeAll
// outer beforeEach
// inner beforeEach
// inner test
// inner afterEach
// outer afterEach
// inner afterAll
// outer afterAll
Scoping
Hooks are scoped to the describe block they’re defined in:
import { describe, test, beforeEach } from "bun:test";
beforeEach(() => {
// Runs before all tests in file
console.log("global beforeEach");
});
describe("suite 1", () => {
beforeEach(() => {
// Only runs before tests in suite 1
console.log("suite 1 beforeEach");
});
test("test 1", () => {});
});
describe("suite 2", () => {
beforeEach(() => {
// Only runs before tests in suite 2
console.log("suite 2 beforeEach");
});
test("test 2", () => {});
});
Timeout
Hooks can have custom timeouts:
import { beforeAll } from "bun:test";
beforeAll(async () => {
// Custom timeout of 30 seconds
await slowSetup();
}, 30000);
Done callback
Hooks support the done callback pattern:
import { beforeEach } from "bun:test";
beforeEach((done) => {
setTimeout(() => {
// Setup complete
done();
}, 100);
});
Prefer using async/await over done callbacks when possible.
Error handling
If a hook throws an error:
beforeAll/beforeEach: The test is marked as failed
afterEach/afterAll: The test result is preserved, but the error is logged
import { beforeEach, test, expect } from "bun:test";
beforeEach(() => {
if (!setupSuccessful()) {
throw new Error("Setup failed");
}
});
test("will fail if setup fails", () => {
expect(true).toBe(true);
});
Resource cleanup with using
Bun supports JavaScript’s using keyword for automatic resource cleanup:
import { test } from "bun:test";
test("automatic cleanup", () => {
using server = createTestServer();
// server.stop() called automatically when test ends
const response = fetch(server.url);
expect(response.status).toBe(200);
});