bun-test-lifecycle
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBun Test Lifecycle
Bun Test 生命周期
Bun supports Jest-compatible lifecycle hooks for test setup and teardown.
Bun 支持与 Jest 兼容的生命周期钩子,用于测试的初始化和清理工作。
Lifecycle Hooks
生命周期钩子
typescript
import { test, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
let db: Database;
let testData: any;
// Runs once before all tests in file
beforeAll(async () => {
db = await Database.connect();
});
// Runs once after all tests in file
afterAll(async () => {
await db.close();
});
// Runs before each test
beforeEach(() => {
testData = { id: 1, name: "test" };
});
// Runs after each test
afterEach(() => {
testData = null;
});
test("example", () => {
expect(testData.name).toBe("test");
});typescript
import { test, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
let db: Database;
let testData: any;
// Runs once before all tests in file
beforeAll(async () => {
db = await Database.connect();
});
// Runs once after all tests in file
afterAll(async () => {
await db.close();
});
// Runs before each test
beforeEach(() => {
testData = { id: 1, name: "test" };
});
// Runs after each test
afterEach(() => {
testData = null;
});
test("example", () => {
expect(testData.name).toBe("test");
});Hook Execution Order
钩子执行顺序
beforeAll
├── beforeEach
│ └── test 1
│ └── afterEach
├── beforeEach
│ └── test 2
│ └── afterEach
afterAllbeforeAll
├── beforeEach
│ └── test 1
│ └── afterEach
├── beforeEach
│ └── test 2
│ └── afterEach
afterAllNested Describe Hooks
嵌套 Describe 钩子
typescript
import { describe, test, beforeAll, beforeEach, afterEach, afterAll } from "bun:test";
describe("outer", () => {
beforeAll(() => console.log("1. outer beforeAll"));
afterAll(() => console.log("6. outer afterAll"));
beforeEach(() => console.log("2. outer beforeEach"));
afterEach(() => console.log("5. outer afterEach"));
describe("inner", () => {
beforeEach(() => console.log("3. inner beforeEach"));
afterEach(() => console.log("4. inner afterEach"));
test("test", () => {
console.log("test runs here");
});
});
});
// Output:
// 1. outer beforeAll
// 2. outer beforeEach
// 3. inner beforeEach
// test runs here
// 4. inner afterEach
// 5. outer afterEach
// 6. outer afterAlltypescript
import { describe, test, beforeAll, beforeEach, afterEach, afterAll } from "bun:test";
describe("outer", () => {
beforeAll(() => console.log("1. outer beforeAll"));
afterAll(() => console.log("6. outer afterAll"));
beforeEach(() => console.log("2. outer beforeEach"));
afterEach(() => console.log("5. outer afterEach"));
describe("inner", () => {
beforeEach(() => console.log("3. inner beforeEach"));
afterEach(() => console.log("4. inner afterEach"));
test("test", () => {
console.log("test runs here");
});
});
});
// Output:
// 1. outer beforeAll
// 2. outer beforeEach
// 3. inner beforeEach
// test runs here
// 4. inner afterEach
// 5. outer afterEach
// 6. outer afterAllAsync Hooks
异步钩子
typescript
beforeAll(async () => {
await setupDatabase();
});
beforeEach(async () => {
await seedTestData();
});
afterEach(async () => {
await clearTestData();
});
afterAll(async () => {
await teardownDatabase();
});typescript
beforeAll(async () => {
await setupDatabase();
});
beforeEach(async () => {
await seedTestData();
});
afterEach(async () => {
await clearTestData();
});
afterAll(async () => {
await teardownDatabase();
});Timeout for Hooks
钩子超时设置
typescript
// Set timeout for slow setup
beforeAll(async () => {
await slowSetup();
}, 30000); // 30 secondstypescript
// Set timeout for slow setup
beforeAll(async () => {
await slowSetup();
}, 30000); // 30 secondsPreload Scripts
预加载脚本
Use for global setup across all test files:
--preloadbash
bun test --preload ./setup.ts使用 实现所有测试文件的全局初始化:
--preloadbash
bun test --preload ./setup.tssetup.ts
setup.ts
typescript
import { beforeAll, afterAll } from "bun:test";
// Global setup
beforeAll(() => {
console.log("Global setup runs before all test files");
});
// Global teardown
afterAll(() => {
console.log("Global teardown runs after all test files");
});
// Set global variables
globalThis.testConfig = {
apiUrl: "http://localhost:3000",
};typescript
import { beforeAll, afterAll } from "bun:test";
// Global setup
beforeAll(() => {
console.log("Global setup runs before all test files");
});
// Global teardown
afterAll(() => {
console.log("Global teardown runs after all test files");
});
// Set global variables
globalThis.testConfig = {
apiUrl: "http://localhost:3000",
};Configure in bunfig.toml
在 bunfig.toml 中配置
toml
[test]
preload = ["./test/setup.ts"]toml
[test]
preload = ["./test/setup.ts"]Common Patterns
常见模式
Database Setup
数据库初始化
typescript
import { beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
let db: Database;
beforeAll(async () => {
db = await Database.connect(process.env.TEST_DB_URL);
await db.migrate();
});
afterAll(async () => {
await db.close();
});
beforeEach(async () => {
await db.beginTransaction();
});
afterEach(async () => {
await db.rollback(); // Reset state
});typescript
import { beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
let db: Database;
beforeAll(async () => {
db = await Database.connect(process.env.TEST_DB_URL);
await db.migrate();
});
afterAll(async () => {
await db.close();
});
beforeEach(async () => {
await db.beginTransaction();
});
afterEach(async () => {
await db.rollback(); // Reset state
});Server Setup
服务端初始化
typescript
import { beforeAll, afterAll } from "bun:test";
let server: Server;
let baseUrl: string;
beforeAll(async () => {
server = Bun.serve({
port: 0, // Random available port
fetch: app.fetch,
});
baseUrl = `http://localhost:${server.port}`;
});
afterAll(() => {
server.stop();
});
test("api works", async () => {
const res = await fetch(`${baseUrl}/api/health`);
expect(res.ok).toBe(true);
});typescript
import { beforeAll, afterAll } from "bun:test";
let server: Server;
let baseUrl: string;
beforeAll(async () => {
server = Bun.serve({
port: 0, // Random available port
fetch: app.fetch,
});
baseUrl = `http://localhost:${server.port}`;
});
afterAll(() => {
server.stop();
});
test("api works", async () => {
const res = await fetch(`${baseUrl}/api/health`);
expect(res.ok).toBe(true);
});Mock Setup
Mock 配置
typescript
import { beforeEach, afterEach, spyOn } from "bun:test";
let fetchSpy: ReturnType<typeof spyOn>;
beforeEach(() => {
fetchSpy = spyOn(global, "fetch").mockResolvedValue(
new Response(JSON.stringify({ ok: true }))
);
});
afterEach(() => {
fetchSpy.mockRestore();
});typescript
import { beforeEach, afterEach, spyOn } from "bun:test";
let fetchSpy: ReturnType<typeof spyOn>;
beforeEach(() => {
fetchSpy = spyOn(global, "fetch").mockResolvedValue(
new Response(JSON.stringify({ ok: true }))
);
});
afterEach(() => {
fetchSpy.mockRestore();
});Environment Variables
环境变量配置
typescript
import { beforeAll, afterAll } from "bun:test";
const originalEnv = process.env;
beforeAll(() => {
process.env = {
...originalEnv,
NODE_ENV: "test",
API_KEY: "test-key",
};
});
afterAll(() => {
process.env = originalEnv;
});typescript
import { beforeAll, afterAll } from "bun:test";
const originalEnv = process.env;
beforeAll(() => {
process.env = {
...originalEnv,
NODE_ENV: "test",
API_KEY: "test-key",
};
});
afterAll(() => {
process.env = originalEnv;
});Shared Fixtures
共享夹具
typescript
// fixtures.ts
export async function createTestUser() {
return { id: 1, name: "Test User" };
}
export async function cleanupTestUser(user: any) {
// cleanup logic
}
// test file
import { createTestUser, cleanupTestUser } from "./fixtures";
let user: any;
beforeEach(async () => {
user = await createTestUser();
});
afterEach(async () => {
await cleanupTestUser(user);
});typescript
// fixtures.ts
export async function createTestUser() {
return { id: 1, name: "Test User" };
}
export async function cleanupTestUser(user: any) {
// cleanup logic
}
// test file
import { createTestUser, cleanupTestUser } from "./fixtures";
let user: any;
beforeEach(async () => {
user = await createTestUser();
});
afterEach(async () => {
await cleanupTestUser(user);
});Hook Errors
钩子错误
If a hook throws, all tests in that describe block fail:
typescript
beforeAll(() => {
throw new Error("Setup failed");
});
// All tests in this file will fail
test("will fail", () => {
expect(true).toBe(true);
});如果钩子抛出异常,该 describe 块中的所有测试都会失败:
typescript
beforeAll(() => {
throw new Error("Setup failed");
});
// All tests in this file will fail
test("will fail", () => {
expect(true).toBe(true);
});Common Errors
常见错误
| Error | Cause | Fix |
|---|---|---|
| Slow async setup | Increase timeout |
| Wrong scope | Check hook placement |
| afterAll skipped | Ensure no throws in tests |
| Missing cleanup | Add proper afterEach |
| 错误 | 原因 | 修复方案 |
|---|---|---|
| 异步初始化过慢 | 增加超时时间 |
| 作用域错误 | 检查钩子放置位置 |
| afterAll 被跳过 | 确保测试中没有抛出异常 |
| 缺失清理步骤 | 添加合适的 afterEach |
When to Load References
何时加载参考文档
Load when:
references/preload-scripts.md- Complex global setup
- Multiple preload files
Load when:
references/fixtures.md- Reusable test fixtures
- Factory patterns
当满足以下条件时加载 :
references/preload-scripts.md- 复杂的全局初始化
- 多个预加载文件
当满足以下条件时加载 :
references/fixtures.md- 可复用的测试夹具
- 工厂模式