bun test 默认单进程跑测试:NODE_ENV=test、UTC 时区与未处理错误会改变退出码
Bun 仓库
bun test 文档
运行时行为
API 参考
配置
bun test 是 Bun 内置的 Jest 兼容测试运行器,测试跑在 Bun 运行时里。支持 TypeScript 和 JSX 直接运行、生命周期钩子、快照测试、UI/DOM 测试,以及 --watch、--preload。API 从 bun:test 导入,写法接近 Jest 的 expect/describe/test。Jest 完全兼容是长期目标,当前只实现了有限的 expect 匹配器集合,完整度见官方 tracking issue。Bun 转译器会自动注入 bun:test 的导入,不像 Jest 注入全局;自动注入只发生在 bun test 期间,且只对测试文件和 preload 脚本生效。
默认运行时行为:单进程、NODE_ENV=test、UTC
运行 bun test 时,测试运行器默认在单个进程里跑所有测试:先加载所有 --preload 脚本,然后在一个共享全局里跑每个文件。这样启动快、能共享内存、调试简单。代价也直接:测试共享全局状态,要用生命周期钩子清理;一个测试崩溃会影响其他测试;单个测试没有真正并行化。要把文件分散到多核,用 --parallel。
两个容易被忽略的运行时行为:
bun test把$NODE_ENV设为"test",除非环境里或.env已经设置过。- 默认时区用 UTC(
Etc/UTC),除非TZ覆盖。这样跨机器的日期时间行为一致。
未处理的 promise rejection 和测试之间的错误会被跟踪。即使所有测试都通过,只要出现未处理错误,退出码也会非零,数值等于这些错误数量。退出码语义:0 表示全通过;非 0 表示失败。过滤时如果没有测试匹配 filter,退出码为 1。
测试发现与过滤
运行命令就是 bun test。测试文件发现规则会递归搜索工作目录:
*.test.{js|jsx|ts|tsx|mjs|cjs|mts|cts}*_test.{...}*.spec.{...}*_spec.{...}
过滤分两种。位置参数按文件路径过滤,注意它不是 glob;路径要以 ./ 或 / 开头才算文件,否则会被当成 filter。-t/--test-name-pattern 按测试名过滤。
超时、并发、重跑与随机化
每个测试默认超时 5000ms,超过即失败。--timeout 全局改,test 的第三个参数可以改单个测试,0 或 Infinity 禁用超时。超时会抛不可捕获异常强制停止,并杀死测试里 spawn 的子进程,避免后台残留僵尸进程。
并发方面:test.concurrent 或 --concurrent 让同一文件内的异步测试并行。加了 --concurrent 后,除非标 test.serial,否则全部并行。跨文件并行用 --parallel。--rerun-each 可以多次重跑每个测试来检测不稳定;--randomize 加 seed 随机化顺序,用来找隐藏依赖;--bail 遇到失败即退出。还有 retry/repeats 选项,其中 repeats: N 实际跑 N+1 次。
CLI flags 里,--smol 减少测试运行器 VM 的内存占用;--watch 监视重跑,推荐;--hot 更激进地保留运行间状态;--preload 预加载脚本定义钩子;--todo 跑待办测试。
钩子、mock、快照
生命周期钩子有 beforeAll/beforeEach/afterEach/afterAll,可以定义在测试文件里,也可以用 --preload 的独立文件。mock 可以用 mock 函数、spyOn,另有 vi,这是 Vitest 兼容的 mocking 工具,方便从 Vitest 迁移。还支持 expectTypeOf 类型测试,但它在运行时是空操作,需要单独跑 TypeScript 做类型检查。快照测试支持 toMatchSnapshot / toMatchInlineSnapshot 等。
bunfig.toml 的 [test] 配置
配置写在 bunfig.toml 的 [test] 段。字段包括:
root:指定测试发现根目录。preload:预加载脚本。junit:junit 报告输出路径。smol:对应--smol。concurrentTestGlob:让匹配 glob 的文件并发跑,可以渐进迁移到并发。randomize+seed:随机化顺序。rerunEach:重跑次数。- 覆盖率相关:
coverageThreshold(数字或对象;设置后启用fail_on_low_coverage,覆盖率低于阈值则失败)、coveragePathIgnorePatterns、coverageSkipTestFiles、coverageIgnoreSourcemaps。
另外,[install] 段(registry、cafile、prefer、exact 等)会被 bun test 继承。测试要访问私有 registry 时,这一点很重要。
CI 集成
GitHub Actions 示例:用 oven-sh/setup-bun@v2 安装 Bun,然后执行 bun install、bun test。
边界与踩坑
- 默认单进程共享全局状态。测试之间可能互相污染,用
beforeEach/afterEach清理;需要隔离时再考虑--parallel或--concurrent。 - 位置参数过滤不是 glob。路径必须以
./或/开头,否则会被当作测试名 filter。没有匹配时退出码1。 - 未处理 promise rejection 和测试间错误会让退出码非零,即使断言全过。CI 里不能只看测试通过数。
- 超时默认
5000ms,超时会杀子进程。设0或Infinity禁用超时后,需要自己管理长跑任务和残留进程。 --concurrent会让同文件异步测试默认并行,除非用test.serial标注。有共享状态时注意顺序。--hot比--watch保留更多运行间状态,隔离要求高的测试慎用。expectTypeOf运行时是空操作,类型错误不会在bun test里暴露,需要单独跑 TypeScript。repeats: N实际执行N+1次,不要按 N 次理解。[install]配置会被继承。私有 registry、cafile、prefer、exact等会影响测试安装依赖的行为。