程式碼覆蓋率
使用 Bun 內建的程式碼覆蓋率報告來跟蹤測試覆蓋率並查詢未經測試的程式碼
Bun 的測試執行器內建了程式碼覆蓋率報告功能。使用它可以檢視測試覆蓋了程式碼庫的多少內容,並查詢未經測試的程式碼。
啟用覆蓋率
bun:test 可以報告測試覆蓋了哪些程式碼行。傳遞 --coverage 以在控制台輸出覆蓋率報告:
bun test --coverage
-------------|---------|---------|-------------------
File | % Funcs | % Lines | Uncovered Line #s
-------------|---------|---------|-------------------
All files | 38.89 | 42.11 |
index-0.ts | 33.33 | 36.84 | 10-15,19-24
index-1.ts | 33.33 | 36.84 | 10-15,19-24
index-10.ts | 33.33 | 36.84 | 10-15,19-24
index-2.ts | 33.33 | 36.84 | 10-15,19-24
index-3.ts | 33.33 | 36.84 | 10-15,19-24
index-4.ts | 33.33 | 36.84 | 10-15,19-24
index-5.ts | 33.33 | 36.84 | 10-15,19-24
index-6.ts | 33.33 | 36.84 | 10-15,19-24
index-7.ts | 33.33 | 36.84 | 10-15,19-24
index-8.ts | 33.33 | 36.84 | 10-15,19-24
index-9.ts | 33.33 | 36.84 | 10-15,19-24
index.ts | 100.00 | 100.00 |
-------------|---------|---------|-------------------
預設啟用
要預設啟用覆蓋率報告,請將以下內容新增到你的 bunfig.toml 中:
[test]
# 始終啟用覆蓋率
coverage = true
預設情況下,覆蓋率報告會排除測試檔案並使用源對映。你可以在 bunfig.toml 中對這兩項進行設定。
[test]
coverageSkipTestFiles = false # default true
覆蓋率閾值
在 bunfig.toml 中設定覆蓋率閾值。如果測試套件未達到或超過該閾值,bun test 將以非零退出程式碼退出。
簡單閾值
[test]
# 要求 90% 的行級覆蓋率和函式級覆蓋率
coverageThreshold = 0.9
詳細閾值
[test]
# To set different thresholds for lines and functions
coverageThreshold = { lines = 0.9, functions = 0.9 }
設定任一鍵後,如果行覆蓋率或函式覆蓋率低於相應閾值,bun test --coverage 就會以非零退出程式碼退出;未設定的鍵會保留 0.9 的預設值。Bun 接受 statements 鍵,但目前不會對其進行檢查。在非 --parallel 模式下,只有啟用了 text 報告器(預設設定)時才會進行檢查;如果只使用 --coverage-reporter=lcov 執行,目前無論閾值如何都會以 0 退出。
覆蓋率報告器
預設情況下,Bun 會將覆蓋率報告列印到控制台。
要儲存報告以供 CI 或其他工具使用,請在命令列中傳入 --coverage-reporter=lcov,或在 bunfig.toml 中設定 coverageReporter。
[test]
coverageReporter = ["text", "lcov"] # 預設 ["text"]
coverageDir = "path/to/somewhere" # 預設 "coverage"
可用的報告器
| 報告器 | 說明 |
|---|---|
text | 在控制台列印覆蓋率文本摘要 |
lcov | 以 lcov 格式儲存覆蓋率 |
LCOV 覆蓋率報告器
lcov 報告器會將 lcov.info 檔案寫入覆蓋率目錄。
[test]
coverageReporter = "lcov"
# 或通過 CLI
bun test --coverage --coverage-reporter=lcov
可以讀取 LCOV 格式的工具和服務包括:
- 程式碼編輯器:VS Code 擴充套件可以內聯顯示覆蓋率
- CI/CD 服務:GitHub Actions、GitLab CI、CircleCI
- 覆蓋率服務:Codecov、Coveralls
- 整合開發環境(IDE):WebStorm、IntelliJ IDEA
在 GitHub Actions 中使用 LCOV
name: 帶覆蓋率的測試
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- run: bun test --coverage --coverage-reporter=lcov
- name: 上傳覆蓋率到 Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
從覆蓋率中排除檔案
跳過測試檔案
覆蓋率報告預設排除測試檔案。要包含這些檔案:
[test]
coverageSkipTestFiles = false # default true
當 coverageSkipTestFiles 為 true(預設值)時,覆蓋率報告會排除符合測試檔案模式的檔案(例如 *.test.ts、*.spec.js)。
忽略特定路徑和模式
coveragePathIgnorePatterns 會從覆蓋率報告中排除特定檔案或檔案模式:
[test]
# 單個模式
coveragePathIgnorePatterns = "**/*.spec.ts"
# 多個模式
coveragePathIgnorePatterns = [
"**/*.spec.ts",
"**/*.test.ts",
"src/utils/**",
"*.config.js"
]
該選項接受 glob 模式,工作方式類似於 Jest 的 collectCoverageFrom 忽略模式。Bun 會在文本和 LCOV 輸出中將符合任一模式的檔案排除在覆蓋率計算和報告之外。
常見用例
[test]
coveragePathIgnorePatterns = [
# 排除工具檔案
"src/utils/**",
# 排除設定檔
"*.config.js",
"webpack.config.ts",
"vite.config.ts",
# 排除特定測試模式
"**/*.spec.ts",
"**/*.e2e.ts",
# 排除建置產物
"dist/**",
"build/**",
# 排除生成檔案
"src/generated/**",
"**/*.generated.ts",
# 排除第三方/供應商程式碼
"vendor/**",
"third-party/**"
]
原始碼對映(Sourcemaps)
Bun 預設會轉譯所有檔案,並生成內部源對映,將原始原始碼中的行對映到 Bun 的內部表示。要讓覆蓋率報告忽略此源對映,請將 test.coverageIgnoreSourcemaps 設定為 true。除非用於高階場景,否則通常不需要這樣做。
[test]
coverageIgnoreSourcemaps = true # 預設 false
覆蓋率預設行為
預設情況下,覆蓋率報告:
- 排除
node_modules目錄 - 排除 使用非 JS/TS 載入器載入的檔案(例如
.css、.txt),除非你指定了自定義 JS 載入器 - 排除 測試檔案本身(設定
coverageSkipTestFiles = false可將其納入報告) - 可以通過
coveragePathIgnorePatterns排除其他檔案
高階設定
自定義覆蓋率目錄
[test]
coverageDir = "coverage-reports" # 預設 "coverage"
多個報告器
[test]
coverageReporter = ["text", "lcov"]
針對特定測試模式啟用覆蓋率
# 僅對特定測試檔案執行覆蓋率
bun test --coverage src/components/*.test.ts
# 針對名稱模式執行覆蓋率
bun test --coverage --test-name-pattern="API"
CI/CD 整合
GitHub Actions 範例
name: 覆蓋率報告
on: [push, pull_request]
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: 安裝相依
run: bun install
- name: 執行帶覆蓋率的測試
run: bun test --coverage --coverage-reporter=lcov
- name: 上傳到 Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
fail_ci_if_error: true
GitLab CI 範例
test:coverage:
stage: test
script:
- bun install
- bun test --coverage --coverage-reporter=text --coverage-reporter=lcov
coverage: '/All files\s*\|\s*[\d.]+\s*\|\s*(\d+\.\d+)/'
artifacts:
paths:
- coverage/lcov.info
解析覆蓋率報告
文本輸出說明
-------------|---------|---------|-------------------
File | % Funcs | % Lines | Uncovered Line #s
-------------|---------|---------|-------------------
All files | 85.71 | 90.48 |
src/ | 85.71 | 90.48 |
utils.ts | 100.00 | 100.00 |
api.ts | 75.00 | 85.71 | 15-18,25
main.ts | 80.00 | 88.89 | 42,50-52
-------------|---------|---------|-------------------
- % Funcs:測試期間呼叫的函式百分比
- % Lines:測試期間執行的可執行程式碼行百分比
- Uncovered Line #s:從未執行過的行號
目標標準
- 整體覆蓋率達到 80% 以上:通常被認為是良好水平
- 關鍵路徑覆蓋率達到 90% 以上:重要的業務邏輯應經過充分測試
- 工具函式覆蓋率達到 100%:純函式和工具函式可以得到完整測試
- UI 元件的覆蓋率較低:通常可以接受,因為它們可能需要整合測試
最佳實踐
注重品質而非數量
// 好:測試實際功能
test("calculateTax 應正確處理不同稅率", () => {
expect(calculateTax(100, 0.08)).toBe(8);
expect(calculateTax(100, 0.1)).toBe(10);
expect(calculateTax(0, 0.08)).toBe(0);
});
// 不推薦:僅覆蓋行數
test("calculateTax 存在", () => {
calculateTax(100, 0.08); // 沒有斷言!
});
測試邊界情況
test("使用者輸入驗證", () => {
// 測試正常情況
expect(validateEmail("user@example.com")).toBe(true);
// 測試邊界情況以有效提高覆蓋率
expect(validateEmail("")).toBe(false);
expect(validateEmail("invalid")).toBe(false);
expect(validateEmail(null)).toBe(false);
});
利用覆蓋率發現缺失測試
# 執行覆蓋率,找出未測試程式碼
bun test --coverage
# 關注需要重點關注的檔案
bun test --coverage src/critical-module.ts
結合其它品質指標
覆蓋率只是一個指標。還應考慮:
- 程式碼審查品質
- 整合測試覆蓋率
- 錯誤處理測試
- 效能測試
- 型別安全。
常見問題排查
某些檔案沒顯示覆蓋率
如果檔案沒有出現在覆蓋率報告中,可能是因為你的測試沒有匯入這些檔案。覆蓋率只會跟蹤已載入的檔案。
// 確保匯入你想測試的模組
import { myFunction } from "../src/my-module";
test("我的函式正常工作", () => {
expect(myFunction()).toBeDefined();
});
覆蓋率報告不準確
如果看到的覆蓋率報告不符合預期:
- 檢查源對映是否正常工作
- 驗證
coveragePathIgnorePatterns中的檔案模式 - 確保測試檔案匯入了要測試的程式碼
大型程式碼庫效能問題
對於大型專案,收集覆蓋率可能會導致測試變慢:
[test]
# 排除不需要計算覆蓋率的大型目錄
coveragePathIgnorePatterns = [
"vendor/**",
"generated/**"
]
建議僅在 CI 或特定分支執行覆蓋率,而不是每次本地開發都執行。