執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

測試執行器

Bun 的快速內建 Jest 相容測試執行器,支援 TypeScript、生命週期鉤子、模擬和監視模式

Bun 自帶一個快速且與 Jest 相容的測試執行器。測試在 Bun 執行環境中執行,並支援以下功能。

  • TypeScript 和 JSX
  • 生命週期鉤子
  • 快照測試
  • UI 與 DOM 測試
  • 使用 --watch 的監視模式
  • 使用 --preload 的指令碼預載入

執行測試

terminal
bun test

你可以使用類似 Jest 的 API,以 JavaScript 或 TypeScript 編寫測試。請參閱編寫測試。

math.test.ts
import { expect, test } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});

測試執行器會遞迴搜尋工作目錄中符合以下模式的檔案:

  • *.test.{js|jsx|ts|tsx|mjs|cjs|mts|cts}
  • *_test.{js|jsx|ts|tsx|mjs|cjs|mts|cts}
  • *.spec.{js|jsx|ts|tsx|mjs|cjs|mts|cts}
  • *_spec.{js|jsx|ts|tsx|mjs|cjs|mts|cts}

若要篩選要執行的_測試檔案_,請向 bun test 傳遞其他位置引數。路徑與任一篩選條件匹配的測試檔案都會執行。篩選條件通常是檔名或目錄名;目前尚不支援 glob 模式。

terminal
bun test <filter> <filter> ...

如果要根據 測試名稱 過濾,使用 -t/--test-name-pattern 標誌。

terminal
# 執行名稱中含 "addition" 的所有測試或測試套件
bun test --test-name-pattern addition

若要執行特定檔案,確保路徑以 ./ 或 / 開頭,以區別於篩選名。

terminal
bun test ./test/specific-file.test.ts

預設情況下,測試執行器會在單個行程中執行所有測試:它會載入所有 --preload 指令碼(請參閱生命週期),然後在一個共享全域性環境中執行每個檔案。傳入 --parallel 可將檔案分散到各個 CPU 核心上執行。如果測試失敗,測試執行器會以非零退出程式碼退出。

CI/CD 整合

bun test 支援多種 CI/CD 整合方案。

GitHub Actions

bun test 會自動檢測其是否在 GitHub Actions 中執行,並直接向控制台輸出 GitHub Actions 註釋。

無需額外設定,只需在工作流程中安裝 bun 並執行 bun test。

如何在 GitHub Actions 工作流程中安裝 bun

要在 GitHub Actions 工作流程中使用 bun test,新增如下步驟:

.github/workflows/test.yml
jobs:
  build:
    name: build-app
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install bun
        uses: oven-sh/setup-bun@v2
      - name: Install dependencies # (假設你的專案有相依)
        run: bun install # 如果喜歡,也可以用 npm/yarn/pnpm 代替
      - name: Run tests
        run: bun test

JUnit XML 報告(GitLab 等)

要寫入 JUnit XML 報告,請將 --reporter=junit 與 --reporter-outfile 一起傳入。

terminal
bun test --reporter=junit --reporter-outfile=./bun.xml

bun test 仍會像往常一樣寫入 stdout/stderr,並在執行結束時將 JUnit XML 報告寫入指定路徑。

JUnit XML 是 CI/CD 管道中報告測試結果的流行格式。

超時設定

使用 --timeout 標誌指定每個測試的超時時間,單位為毫秒。如果測試超時,Bun 會將其標記為失敗。預設值為 5000。

terminal
# 預設值是 5000
bun test --timeout 20

並行測試執行

要跨 CPU 核執行測試檔案,請參閱 --parallel。以下標誌控制檔案_內部_測試的並行性。

預設情況下,Bun 會在每個測試檔案中按順序執行所有測試。並行執行會並行執行非同步測試,從而加快包含相互獨立測試的測試套件。

--concurrent 標誌

使用 --concurrent 標誌,實現各測試檔案內部的所有測試並行執行:

terminal
bun test --concurrent

啟用此標誌後,除非測試標記為 test.serial,否則所有測試都會並行執行。

--max-concurrency 標誌

用 --max-concurrency 標誌控制最大並行測試數量:

terminal
# 限制最大並行測試為 4 個
bun test --concurrent --max-concurrency 4

# 預設值是 20
bun test --concurrent

此限制有助於在執行大量並行測試時防止資源耗盡。預設值為 20。

test.concurrent

單獨標記測試為並行執行,即使未使用 --concurrent 也能並行執行:

math.test.ts
import { test, expect } from "bun:test";

// 這些測試相互並行執行
test.concurrent("concurrent test 1", async () => {
  await fetch("/api/endpoint1");
  expect(true).toBe(true);
});

test.concurrent("concurrent test 2", async () => {
  await fetch("/api/endpoint2");
  expect(true).toBe(true);
});

// 此測試順序執行
test("sequential test", () => {
  expect(1 + 1).toBe(2);
});

test.serial

即使啟用 --concurrent 標誌,也強制測試順序執行:

math.test.ts
import { test, expect } from "bun:test";

let sharedState = 0;

// 這些測試必須順序執行
test.serial("first serial test", () => {
  sharedState = 1;
  expect(sharedState).toBe(1);
});

test.serial("second serial test", () => {
  // 相依前一個測試
  expect(sharedState).toBe(1);
  sharedState = 2;
});

// 如果啟用 --concurrent,此測試可並行執行
test("independent test", () => {
  expect(true).toBe(true);
});

// 測試限定符鏈式呼叫
test.failing.each([1, 2, 3])("chained qualifiers %d", input => {
  expect(input).toBe(0); // 對每個輸入,該測試預期失敗
});

重試失敗的測試

使用 --retry 標誌自動重試失敗的測試,最多重試指定次數。如果測試失敗後在後續嘗試中通過,Bun 會將其報告為通過。

terminal
bun test --retry 3

每個測試可以通過 { retry: N } 設定覆蓋全域性的 --retry 值:

TypeScript
// 使用全域性 --retry 值
test("uses global retry", () => {
  /* ... */
});

// 使用自己的重試次數覆蓋 --retry
test("custom retry", { retry: 1 }, () => {
  /* ... */
});

你也可以在 bunfig.toml 中設定:

bunfig.toml
[test]
retry = 3

重新執行測試

使用 --rerun-each 標誌多次執行每個測試。這有助於發現不穩定或非確定性的測試失敗。

terminal
bun test --rerun-each 100

隨機化測試執行順序

使用 --randomize 標誌以隨機順序執行測試,有助於發現相依共享狀態或執行順序的測試。

terminal
bun test --randomize

使用 --randomize 時,Bun 會在測試摘要中顯示用於隨機化的種子:

terminal
bun test --randomize
Text
# ... 測試輸出 ...
 --seed=12345
 2 pass
 8 fail
Ran 10 tests across 2 files. [50.00ms]

使用 --seed 實現可復現的隨機順序

使用 --seed 標誌指定隨機化種子,以便在除錯相依執行順序的失敗測試時復現相同的測試順序。

terminal
# 復現之前的隨機執行順序
bun test --seed 123456

--seed 標誌會隱式啟用 --randomize,因此無需同時指定這兩個標誌。相同的種子始終會產生相同的測試執行順序。

失敗即停止 --bail

使用 --bail 標誌,在測試失敗達到指定次數後中止測試執行。預設情況下,Bun 會執行所有測試並報告所有失敗,但在 CI 中,提前停止測試並減少 CPU 使用量可能更為合適。

terminal
# 發生 1 次失敗即停止
bun test --bail

# 發生 10 次失敗即停止
bun test --bail=10

監視模式

與 bun run 類似,bun test 接受 --watch 標誌,以監視更改並重新執行測試。

terminal
bun test --watch

生命週期鉤子

Bun 支援以下生命週期鉤子:

鉤子描述
beforeAll所有測試開始前執行一次
beforeEach每個測試開始前執行
afterEach每個測試結束後執行
afterAll所有測試完成後執行一次

在測試檔案中定義鉤子,或在使用 --preload 標誌預載入的單獨檔案中定義鉤子。

terminal
bun test --preload ./setup.ts

請參閱生命週期。

模擬

使用 mock 函式建立模擬函式。

math.test.ts
import { test, expect, mock } from "bun:test";
const random = mock(() => Math.random());

test("random", () => {
  const val = random();
  expect(val).toBeGreaterThan(0);
  expect(random).toHaveBeenCalled();
  expect(random).toHaveBeenCalledTimes(1);
});

或者使用 jest.fn();其行為完全相同。

math.test.ts
import { test, expect, mock } from "bun:test"; // [!code --]
import { test, expect, jest } from "bun:test"; // [!code ++]

const random = mock(() => Math.random()); // [!code --]
const random = jest.fn(() => Math.random()); // [!code ++]

請參閱模擬。

快照測試

bun test 支援快照測試。

math.test.ts
// toMatchSnapshot 範例用法
import { test, expect } from "bun:test";

test("snapshot", () => {
  expect({ a: 1 }).toMatchSnapshot();
});

要更新快照,請使用 --update-snapshots 標誌。

terminal
bun test --update-snapshots

請參閱快照。

UI 與 DOM 測試

Bun 相容流行的 UI 測試庫:

請參閱 DOM 測試。

大型程式碼庫

對於包含數千個測試檔案的測試套件,bun test 提供了多個可組合使用的調優選項:工作行程、隔離級別、跨機器分片,以及基於執行時長的排程。並行與隔離測試執行深入介紹了每一項。以下按大致收益順序介紹它們的配合方式:

1. 使用每個核心:--parallel。 每個核心使用一個工作行程,檔案一次分配一個。

2. 決定需要多少隔離。 --parallel 會為每個檔案提供全新的全域性環境,這是安全的預設設定,也是 Jest/Vitest 的做法。如果檔案之間不會洩漏狀態(它們在共享同一個全域性環境的普通 bun test 下已經可以通過),--parallel --no-isolate 可讓每個工作行程只評估一次匯入和預載入指令碼,而不是每個檔案評估一次。對於由許多小檔案組成的測試套件,這通常是最大的效能提升。請參閱對比方式。

3. 跨機器拆分:--shard=i/n。 確定性拆分,無需協調器。每個 CI 作業執行一個分片,而每個分片仍會在本地使用 --parallel。

4. 按耗時而非數量均衡:--timings。 有了記錄的耗時資料後,Bun 會拆分分片,使每個分片的總耗時大致相同。拆分採用最長處理時間優先的方式,同時將路徑相鄰的檔案放在一起,以保持工作行程的模組快取處於預熱狀態。每個工作行程會先啟動耗時最長的檔案,空閒工作行程則會接手剩餘檔案中耗時最長的檔案。這樣,偶然最後啟動的某個長檔案就不會拖慢整個執行過程。

5. 自動保持耗時資料最新:--update-timings。 每個分片會寫入其執行檔案的耗時;下一次執行會讀取所有這些資料。在 GitHub Actions 中,看起來如下:

.github/workflows/test.yml
jobs:
  test:
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      # last successful run's per-shard timings (nothing on the very first run)
      - uses: actions/cache/restore@v4
        with:
          path: .bun-test-timings
          key: bun-test-timings-${{ github.run_id }}
          restore-keys: bun-test-timings-
      - run: |
          bun test --parallel --shard=${{ matrix.shard }}/4 --update-timings \
            --timings=.bun-test-timings/next/${{ matrix.shard }}.json \
            $(ls .bun-test-timings/*.json 2>/dev/null | sed 's/^/--timings=/')
      - uses: actions/upload-artifact@v4
        with:
          name: timings-${{ matrix.shard }}
          path: .bun-test-timings/next/${{ matrix.shard }}.json
  save-timings:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: timings-*
          path: .bun-test-timings
          merge-multiple: true
      - uses: actions/cache/save@v4
        with:
          path: .bun-test-timings
          key: bun-test-timings-${{ github.run_id }}

每個分片必須讀取相同的一組耗時檔案,這樣各分片的耗時才能彙總為整個測試套件的耗時。因此,一次執行會讀取上次執行的檔案(從快取中恢復),並將自身的檔案寫入其他仍在執行的同級分片無法讀取的位置(如上方的 next/)。如果第 2 步適用於你,請在 bun test 命令中新增 --no-isolate。

6. 在檔案內部:test.concurrent 適用於將大部分時間花在等待上的 I/O 密集型測試。

效能

Bun 的測試執行器非常快速。

執行 266 個 React SSR 測試,速度快於 Jest 列印版本號。

AI 代理整合

當你將 Bun 的測試執行器與 AI 程式設計助手結合使用時,可以啟用更安靜的輸出,在保留失敗詳情的同時去除其餘噪音。

環境變數

設定以下任一環境變數即可啟用適合 AI 的輸出:

  • CLAUDECODE=1 - 適用於 Claude Code
  • REPL_ID=1 - 適用於 Replit
  • AGENT=1 - 通用 AI 代理標誌

行為

當 Bun 檢測到 AI 代理環境時:

  • 只詳細顯示測試失敗資訊
  • 隱藏通過、跳過及待辦測試指示符
  • 保留統計摘要
terminal
# 範例:為 Claude Code 啟用安靜輸出
CLAUDECODE=1 bun test

# 仍顯示失敗和摘要,隱藏通過測試的詳細輸出

CLI 用法

bash
bun test <patterns>

執行控制

--timeoutnumberdefault 5000

設定每個測試的超時時間,單位為毫秒(預設 5000)

--rerun-eachnumber

重新執行每個測試檔案 NUMBER 次,以幫助發現某些錯誤

--retrynumber

最多重試失敗的測試 NUMBER 次。每個測試的 { retry: N } 會覆蓋此標誌

--concurrentboolean

將所有測試視為 test.concurrent() 測試

--randomizeboolean

以隨機順序執行測試

--seednumber

設定測試隨機化的隨機種子

--bailnumber

失敗 NUMBER 次後退出測試套件。如果不指定數字,則預設為 1

--max-concurrencynumberdefault 20

最大同時執行的測試數量(預設 20)

測試過濾

--todoboolean

包含標記為 test.todo() 的測試

--test-name-patternstring

只執行名稱匹配給定正規表示式的測試。別名:-t

報告

--reporterstring

測試輸出報告格式。可用選項:junit(需要 --reporter-outfile)、dots。預設:控制台輸出。

--reporter-outfilestring

報告格式的輸出檔案路徑(與 --reporter=junit 一起使用時必需)

--dotsboolean

啟用點狀報告。--reporter=dots 的簡寫

覆蓋率

--coverageboolean

生成覆蓋率報告

--coverage-reporterstringdefault text

以 text 和/或 lcov 格式報告覆蓋率。預設 text

--coverage-dirstringdefault coverage

覆蓋率檔案的目錄。預設 coverage

快照

--update-snapshotsboolean

更新快照檔案。別名:-u

範例

執行所有測試檔案:

terminal
bun test

執行所有檔名包含 "foo" 或 "bar" 的測試檔案:

terminal
bun test foo bar

執行所有測試檔案,僅包含名稱中含有 "baz" 的測試:

terminal
bun test --test-name-pattern baz