Skip to content

測試運行器支持以下生命周期鉤子。這對於加載測試夾具、模擬數據和配置測試環境很有用。

鉤子描述
beforeAll在所有測試之前運行一次。
beforeEach在每個測試之前運行。
afterEach在每個測試之後運行。
afterAll在所有測試之後運行一次。
onTestFinished在單個測試完成後運行(在所有 afterEach 之後)。

每個測試的設置和清理

使用 beforeEachafterEach 執行每個測試的設置和清理邏輯。

ts
import { beforeEach, afterEach, test } from "bun:test";

beforeEach(() => {
  console.log("運行測試。");
});

afterEach(() => {
  console.log("測試完成。");
});

// 測試...
test("示例測試", () => {
  // 此測試將在之前運行 beforeEach
  // 並在之後運行 afterEach
});

每個范圍的設置和清理

使用 beforeAllafterAll 執行每個范圍的設置和清理邏輯。范圍由鉤子定義的位置決定。

限定到 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();
});

Bun學習網由www.bunjs.com.cn整理維護