核心工具链测试运行器

测试运行器

使用 bun:test 编写测试、模拟依赖并在 CI 中稳定执行

最后更新于

最小测试

sum.test.ts
import { describe, expect, test } from 'bun:test';

describe('sum', () => {
  test('adds values', () => {
    expect(2 + 3).toBe(5);
  });
});
bun test
bun test sum
bun test --watch
bun test --coverage

Bun 自动发现常见的 *.test.**_test.**.spec.**_spec.* 文件。

生命周期与异步测试

import { afterEach, beforeEach, expect, test } from 'bun:test';

let controller: AbortController;

beforeEach(() => {
  controller = new AbortController();
});

afterEach(() => {
  controller.abort();
});

test('loads data', async () => {
  const response = await fetch('https://example.com', {
    signal: controller.signal,
  });
  expect(response.ok).toBe(true);
});

真实项目应模拟外部网络,保证测试可重复且不会产生费用或写入生产数据。

Mock 原则

bun:test 提供 mock()spyOn() 和模块模拟。优先顺序:

  1. 纯函数直接测输入输出。
  2. 用依赖注入替换网络、时钟、随机数和数据库。
  3. 只有模块边界难以改造时才使用模块 mock。
  4. 每个测试后恢复状态,避免顺序依赖。

隔离、并行与 CI 分片

Bun 1.3.13 起提供面向大型测试套件的文件级隔离和并行能力:

# 本地发现跨文件状态泄漏
bun test --isolate

# CI 内按 CPU 并行;--parallel 会隐含 --isolate
bun test --parallel --coverage

# 三个独立 CI Job
bun test --shard=1/3
bun test --shard=2/3
bun test --shard=3/3

数据库集成测试、固定端口和共享临时目录可能无法安全并行。为每个 Worker 分配独立数据库/schema、端口和临时目录,或把这组测试保留为串行任务。

覆盖率与不稳定测试

bunfig.toml
[test]
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "./coverage"
coverageSkipTestFiles = true
coverageThreshold = 0.8

阈值不达标时测试进程返回非零状态。覆盖率只能证明代码被执行,不能证明断言有效;关键安全、权限和迁移路径仍需明确场景。

bun test --randomize             # 发现顺序依赖并记录 seed
bun test --seed 12345            # 重现顺序
bun test --retry 2               # 仅用于已知瞬态边界
bun test --rerun-each 20         # 主动暴露不稳定测试

不要用无限重试掩盖确定性缺陷。CI 应记录失败 seed、分片和 Bun 版本。

给编码 Agent 的紧凑输出

AGENT=1 bun test

Bun 会隐藏通过测试的逐条噪声,但保留失败详情和摘要。安静输出不会改变退出码,也不能替代保存完整 CI 日志。

从 Jest 迁移

Bun 追求 Jest 兼容,但迁移仍应核对:

  • 自定义 test environment 和 DOM 环境;
  • 复杂 fake timers;
  • Jest 专属 transformer;
  • 原生扩展和依赖 Node.js loader 的设置;
  • snapshot 路径与 CI 更新策略。
bun test --bail

CI 中可用 --bail 快速失败,但本地排查时通常希望看到完整失败列表。

官方参考:Test runnerTest configurationBun 1.3.13