← 中文资源

简体中文版本

优先为错误路径编写单元测试

按依赖盘点失败模式,在测试中逐一强制触发,并断言用户在出错时真正看到的可观测行为。

阅读英文原文

目标

快乐路径被测试,因为它容易想到;失败分支裸奔上线,因为它容易被忘掉。修法是机械的:盘点每个依赖、枚举失败模式、在测试中逐个强制触发、断言用户可观察的行为。白屏之所以发生,是因为从没人决定超时时该渲染什么,而测试会强迫把这个决定写进代码。

步骤要点

  1. 给目标模块列依赖失败模式清单:网关的超时/5xx/429/坏响应体、仓库的约束冲突/断连、缓存的未命中/宕机。
  2. 每种失败用 mockRejectedValue 确定性触发,跑进真实分支。
  3. 断言可观察契约而非异常类型:status=retryable、文案可重试、购物车未丢。
  4. 补“失败的失败”:日志系统也挂时用户仍能拿到可关联的 traceId。
  5. 用分支覆盖跟踪进展,未覆盖清单应收缩到真正不可达的防御代码。

排错提示

mock 形状漂移就做依赖的契约测试,或录制一次真实错误对象回放进 mock;错误分支不可达说明代码在内部吞错,先重构为返回状态或抛类型化错误才可断言;团队习惯只写快乐路径,就把失败清单变成评审检查项,加依赖不带失败测试的 PR 不予合并。

适用边界

单层的强制触发验证处理逻辑,真实错误形状仍需集成层故障注入;错误路径测试固化的是“预期的降级”,若没人决定超时的 UX,测试会响亮暴露这个空洞。

代码示例

it("renders retryable state when the gateway times out", async () => {
  gateway.charge.mockRejectedValue(new TimeoutError("gateway"));
  const state = await checkout(cart);
  expect(state.status).toBe("retryable");
  expect(state.message).toMatch(/try again/i);
});