目标
随机输入让测试更强壮,却也让失败失去意义——除非随机性被播种、且种子在失败后依然可查。一个生成器、一个环境变量、一行控制台输出,就能把“CI 里出现过一次的诡异失败”变成任何人在本地都能精确重放的脚本,让不可复现不再是关闭缺陷单的理由。
步骤要点
- 先用 rg 审计套件里所有直接调用 Math.random、crypto 随机的地方,这些是可复现性的泄漏点。
- 实现一个以环境变量 TEST_SEED 为种子的 xorshift 生成器,启动时把种子打印到 stderr,所有随机辅助函数都从它派生。
- 把隐式随机源一并纳入种子体系:随机 UUID、当前时间、测试执行顺序都改成种子的函数,重放才能精确而非近似。
- 故意构造只在特定种子下失败的断言,验证用日志里的种子重跑能逐字复现同样的失败。
- 让 CI 失败时自动提取日志中的种子并带种子重跑,把可重放的痕迹固化进构建产物。
排错提示
播了种子仍不可复现,说明还剩异步时序、网络或库内随机,继续搜直连调用并在 setup 里全局打桩 Date 与 Math.random;库自带种子机制就转发给它(fast-check 的 seed、jest 的 randomSeed),别维护两套系统,必要时同时打印两个种子;并行 worker 各拿不同种子时,从主种子加 worker 索引确定性派生并记录,单条失败能在对应 worker 语境重放。
适用边界
种子控制输入不控制墙上时钟,要完全确定性需配合假定时器;重放保真到代码读取真熵(加密随机令牌、OS 随机)为止,这些必须在测试 setup 里打桩。
代码示例
# The failure log now carries its own replay key
npx vitest run tests/checkout.test.ts
# [rand] seed=1729664701234 (replay with TEST_SEED=1729664701234)
# × applies coupon to random cart (expected 850, got 925)
# Replay exactly
TEST_SEED=1729664701234 npx vitest run tests/checkout.test.ts
# × applies coupon to random cart (expected 850, got 925) <-- identical