运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

代码覆盖率

使用 Bun 内置的代码覆盖率报告来跟踪测试覆盖率并查找未经测试的代码

Bun 的测试运行器内置了代码覆盖率报告功能。使用它可以查看测试覆盖了代码库的多少内容,并查找未经测试的代码。

启用覆盖率

bun:test 可以报告测试覆盖了哪些代码行。传递 --coverage 以在控制台输出覆盖率报告:

terminal
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 中:

bunfig.toml
[test]
# 始终启用覆盖率
coverage = true

默认情况下,覆盖率报告会排除测试文件并使用源映射。你可以在 bunfig.toml 中对这两项进行配置。

bunfig.toml
[test]
coverageSkipTestFiles = false  # default true

覆盖率阈值

在 bunfig.toml 中设置覆盖率阈值。如果测试套件未达到或超过该阈值,bun test 将以非零退出代码退出。

简单阈值

bunfig.toml
[test]
# 要求 90% 的行级覆盖率和函数级覆盖率
coverageThreshold = 0.9

详细阈值

bunfig.toml
[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。

bunfig.toml
[test]
coverageReporter = ["text", "lcov"]  # 默认 ["text"]
coverageDir = "path/to/somewhere"    # 默认 "coverage"

可用的报告器

报告器说明
text在控制台打印覆盖率文本摘要
lcov以 lcov 格式保存覆盖率

LCOV 覆盖率报告器

lcov 报告器会将 lcov.info 文件写入覆盖率目录。

bunfig.toml
[test]
coverageReporter = "lcov"
terminal
# 或通过 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

.github/workflows/test.yml
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

从覆盖率中排除文件

跳过测试文件

覆盖率报告默认排除测试文件。要包含这些文件:

bunfig.toml
[test]
coverageSkipTestFiles = false  # default true

当 coverageSkipTestFiles 为 true(默认值)时,覆盖率报告会排除符合测试文件模式的文件(例如 *.test.ts、*.spec.js)。

忽略特定路径和模式

coveragePathIgnorePatterns 会从覆盖率报告中排除特定文件或文件模式:

bunfig.toml
[test]
# 单个模式
coveragePathIgnorePatterns = "**/*.spec.ts"

# 多个模式
coveragePathIgnorePatterns = [
  "**/*.spec.ts",
  "**/*.test.ts",
  "src/utils/**",
  "*.config.js"
]

该选项接受 glob 模式,工作方式类似于 Jest 的 collectCoverageFrom 忽略模式。Bun 会在文本和 LCOV 输出中将符合任一模式的文件排除在覆盖率计算和报告之外。

常见用例

bunfig.toml
[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。除非用于高级场景,否则通常不需要这样做。

bunfig.toml
[test]
coverageIgnoreSourcemaps = true  # 默认 false

覆盖率默认行为

默认情况下,覆盖率报告:

  • 排除 node_modules 目录
  • 排除 使用非 JS/TS 加载器加载的文件(例如 .css、.txt),除非你指定了自定义 JS 加载器
  • 排除 测试文件本身(设置 coverageSkipTestFiles = false 可将其纳入报告)
  • 可以通过 coveragePathIgnorePatterns 排除其他文件

高级配置

自定义覆盖率目录

bunfig.toml
[test]
coverageDir = "coverage-reports"  # 默认 "coverage"

多个报告器

bunfig.toml
[test]
coverageReporter = ["text", "lcov"]

针对特定测试模式启用覆盖率

terminal
# 仅对特定测试文件运行覆盖率
bun test --coverage src/components/*.test.ts

# 针对名称模式运行覆盖率
bun test --coverage --test-name-pattern="API"

CI/CD 集成

GitHub Actions 示例

.github/workflows/coverage.yml
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 示例

.gitlab-ci.yml
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

解析覆盖率报告

文本输出说明

Code
-------------|---------|---------|-------------------
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.ts
// 好:测试实际功能
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.ts
test("用户输入验证", () => {
  // 测试正常情况
  expect(validateEmail("user@example.com")).toBe(true);

  // 测试边界情况以有效提高覆盖率
  expect(validateEmail("")).toBe(false);
  expect(validateEmail("invalid")).toBe(false);
  expect(validateEmail(null)).toBe(false);
});

利用覆盖率发现缺失测试

terminal
# 运行覆盖率,找出未测试代码
bun test --coverage

# 关注需要重点关注的文件
bun test --coverage src/critical-module.ts

结合其它质量指标

覆盖率只是一个指标。还应考虑:

  • 代码审查质量
  • 集成测试覆盖率
  • 错误处理测试
  • 性能测试
  • 类型安全。

常见问题排查

某些文件没显示覆盖率

如果文件没有出现在覆盖率报告中,可能是因为你的测试没有导入这些文件。覆盖率只会跟踪已加载的文件。

test.ts
// 确保导入你想测试的模块
import { myFunction } from "../src/my-module";

test("我的函数正常工作", () => {
  expect(myFunction()).toBeDefined();
});

覆盖率报告不准确

如果看到的覆盖率报告不符合预期:

  1. 检查源映射是否正常工作
  2. 验证 coveragePathIgnorePatterns 中的文件模式
  3. 确保测试文件导入了要测试的代码

大型代码库性能问题

对于大型项目,收集覆盖率可能会导致测试变慢:

bunfig.toml
[test]
# 排除不需要计算覆盖率的大型目录
coveragePathIgnorePatterns = [
  "vendor/**",
  "generated/**"
]

建议仅在 CI 或特定分支运行覆盖率,而不是每次本地开发都运行。