測試運行器支持以下生命周期鉤子。這對於加載測試夾具、模擬數據和配置測試環境很有用。
| 鉤子 | 描述 |
|---|---|
beforeAll | 在所有測試之前運行一次。 |
beforeEach | 在每個測試之前運行。 |
afterEach | 在每個測試之後運行。 |
afterAll | 在所有測試之後運行一次。 |
onTestFinished | 在單個測試完成後運行(在所有 afterEach 之後)。 |
每個測試的設置和清理
使用 beforeEach 和 afterEach 執行每個測試的設置和清理邏輯。
ts
import { beforeEach, afterEach, test } from "bun:test";
beforeEach(() => {
console.log("運行測試。");
});
afterEach(() => {
console.log("測試完成。");
});
// 測試...
test("示例測試", () => {
// 此測試將在之前運行 beforeEach
// 並在之後運行 afterEach
});每個范圍的設置和清理
使用 beforeAll 和 afterAll 執行每個范圍的設置和清理邏輯。范圍由鉤子定義的位置決定。
限定到 Describe 塊
要將鉤子限定到特定的 describe 塊:
ts
import { describe, beforeAll, afterAll, test } from "bun:test";
describe("測試組", () => {
beforeAll(() => {
// 為此 describe 塊設置
console.log("設置測試組");
});
afterAll(() => {
// 為此 describe 塊清理
console.log("清理測試組");
});
test("測試 1", () => {
// 測試實現
});
test("測試 2", () => {
// 測試實現
});
});限定到測試文件
要將鉤子限定到整個測試文件:
ts
import { describe, beforeAll, afterAll, test } from "bun:test";
beforeAll(() => {
// 為整個文件設置
console.log("設置測試文件");
});
afterAll(() => {
// 為整個文件清理
console.log("清理測試文件");
});
describe("測試組", () => {
test("測試 1", () => {
// 測試實現
});
});onTestFinished
使用 onTestFinished 在單個測試完成後運行回調。它在所有 afterEach 鉤子之後運行。
ts
import { test, onTestFinished } from "bun:test";
test("測試後清理", () => {
onTestFinished(() => {
// 在所有 afterEach 鉤子之後運行
console.log("測試完成");
});
});不支持並發測試;請改用 test.serial。
全局設置和清理
要將鉤子限定到整個多文件測試運行,請在單獨的文件中定義鉤子。
ts
import { beforeAll, afterAll } from "bun:test";
beforeAll(() => {
// 全局設置
console.log("全局測試設置");
// 初始化數據庫連接、啟動服務器等
});
afterAll(() => {
// 全局清理
console.log("全局測試清理");
// 關閉數據庫連接、停止服務器等
});然後使用 --preload 在任何測試文件之前運行設置腳本。
bash
bun test --preload ./setup.ts為避免每次運行測試時都輸入 --preload,可以將其添加到 bunfig.toml 中:
toml
[test]
preload = ["./setup.ts"]實用示例
數據庫設置
ts
import { beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
import { createConnection, closeConnection, clearDatabase } from "./db";
let connection;
beforeAll(async () => {
// 連接到測試數據庫
connection = await createConnection({
host: "localhost",
database: "test_db",
});
});
afterAll(async () => {
// 關閉數據庫連接
await closeConnection(connection);
});
beforeEach(async () => {
// 每個測試從干淨的數據庫開始
await clearDatabase(connection);
});API 服務器設置
ts
import { beforeAll, afterAll } from "bun:test";
import { startServer, stopServer } from "./server";
let server;
beforeAll(async () => {
// 啟動測試服務器
server = await startServer({
port: 3001,
env: "test",
});
});
afterAll(async () => {
// 停止測試服務器
await stopServer(server);
});模擬設置
ts
import { beforeEach, afterEach } from "bun:test";
import { mock } from "bun:test";
beforeEach(() => {
// 設置常見模擬
mock.module("./api-client", () => ({
fetchUser: mock(() => Promise.resolve({ id: 1, name: "Test User" })),
createUser: mock(() => Promise.resolve({ id: 2 })),
}));
});
afterEach(() => {
// 每個測試後清除所有模擬
mock.restore();
});異步生命周期鉤子
所有生命周期鉤子都支持異步函數:
ts
import { beforeAll, afterAll, test } from "bun:test";
beforeAll(async () => {
// 異步設置
await new Promise(resolve => setTimeout(resolve, 100));
console.log("異步設置完成");
});
afterAll(async () => {
// 異步清理
await new Promise(resolve => setTimeout(resolve, 100));
console.log("異步清理完成");
});
test("異步測試", async () => {
// 測試將等待 beforeAll 完成
await expect(Promise.resolve("test")).resolves.toBe("test");
});嵌套鉤子
鉤子可以嵌套,並將按適當的順序運行:
ts
import { describe, beforeAll, beforeEach, afterEach, afterAll, test } from "bun:test";
beforeAll(() => console.log("文件 beforeAll"));
afterAll(() => console.log("文件 afterAll"));
describe("外部 describe", () => {
beforeAll(() => console.log("外部 beforeAll"));
beforeEach(() => console.log("外部 beforeEach"));
afterEach(() => console.log("外部 afterEach"));
afterAll(() => console.log("外部 afterAll"));
describe("內部 describe", () => {
beforeAll(() => console.log("內部 beforeAll"));
beforeEach(() => console.log("內部 beforeEach"));
afterEach(() => console.log("內部 afterEach"));
afterAll(() => console.log("內部 afterAll"));
test("嵌套測試", () => {
console.log("測試運行");
});
});
});txt
// 輸出順序:
// 文件 beforeAll
// 外部 beforeAll
// 內部 beforeAll
// 外部 beforeEach
// 內部 beforeEach
// 測試運行
// 內部 afterEach
// 外部 afterEach
// 內部 afterAll
// 外部 afterAll
// 文件 afterAll錯誤處理
如果生命周期鉤子拋出錯誤,它將影響測試執行:
ts
import { beforeAll, test } from "bun:test";
beforeAll(() => {
// 如果此處理拋出錯誤,此范圍中的所有測試將被跳過
throw new Error("設置失敗");
});
test("此測試將被跳過", () => {
// 這不會運行,因為 beforeAll 失敗
});為了更好的錯誤處理:
ts
import { beforeAll, test, expect } from "bun:test";
beforeAll(async () => {
try {
await setupDatabase();
} catch (error) {
console.error("數據庫設置失敗:", error);
throw error; // 重新拋出以使測試套件失敗
}
});最佳實踐
保持鉤子簡單
ts
// 好:簡單、專注的設置
beforeEach(() => {
clearLocalStorage();
resetMocks();
});
// 避免:鉤子中的復雜邏輯
beforeEach(async () => {
// 太多復雜邏輯使測試難以調試
const data = await fetchComplexData();
await processData(data);
await setupMultipleServices(data);
});使用適當的范圍
ts
// 好:用於共享資源的文件級設置
beforeAll(async () => {
await startTestServer();
});
// 好:用於特定於測試的狀態的測試級設置
beforeEach(() => {
user = createTestUser();
});清理資源
ts
import { afterAll, afterEach } from "bun:test";
afterEach(() => {
// 每個測試後清理
document.body.innerHTML = "";
localStorage.clear();
});
afterAll(async () => {
// 清理昂貴的資源
await closeDatabase();
await stopServer();
});