编程 【tag需求】bun test 默认单进程跑测试:NODE_ENV=test、UTC 时区与未处理错误会改变退出码

2026-09-14 00:04:09

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 的第三个参数可以改单个测试,0Infinity 禁用超时。超时会抛不可捕获异常强制停止,并杀死测试里 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,覆盖率低于阈值则失败)、coveragePathIgnorePatternscoverageSkipTestFilescoverageIgnoreSourcemaps

另外,[install] 段(registrycafilepreferexact 等)会被 bun test 继承。测试要访问私有 registry 时,这一点很重要。

CI 集成

GitHub Actions 示例:用 oven-sh/setup-bun@v2 安装 Bun,然后执行 bun installbun test

边界与踩坑

  • 默认单进程共享全局状态。测试之间可能互相污染,用 beforeEach/afterEach 清理;需要隔离时再考虑 --parallel--concurrent
  • 位置参数过滤不是 glob。路径必须以 .// 开头,否则会被当作测试名 filter。没有匹配时退出码 1
  • 未处理 promise rejection 和测试间错误会让退出码非零,即使断言全过。CI 里不能只看测试通过数。
  • 超时默认 5000ms,超时会杀子进程。设 0Infinity 禁用超时后,需要自己管理长跑任务和残留进程。
  • --concurrent 会让同文件异步测试默认并行,除非用 test.serial 标注。有共享状态时注意顺序。
  • --hot--watch 保留更多运行间状态,隔离要求高的测试慎用。
  • expectTypeOf 运行时是空操作,类型错误不会在 bun test 里暴露,需要单独跑 TypeScript。
  • repeats: N 实际执行 N+1 次,不要按 N 次理解。
  • [install] 配置会被继承。私有 registry、cafilepreferexact 等会影响测试安装依赖的行为。
复制全文 生成海报 bun test Bun 测试 单元测试 JavaScript TypeScript

推荐文章

程序员茄子在线接单