测试异步重试任务需控制时间、模拟失败、验证重试行为及最终结果,覆盖全部失败、第N次成功、取消重试等场景,用fake timers推进时间并断言调用次数与错误类型。

测试带有重试机制的异步任务,核心是**控制时间、模拟失败、验证重试行为和最终结果**。不能只测“成功一次”,必须覆盖失败后重试、达到最大重试次数仍失败、中间某次成功等关键路径。
用 sinon.stub 或 jest.mock 模拟不稳定接口
真实接口不可控,需替换成可编程的模拟函数,让它按需返回成功或失败:
- 用 sinon.stub() 返回 Promise 并手动 reject/reslove;例如前两次 reject,第三次 resolve
- 用 jest.fn() 配合 mockImplementationOnce 实现“调用序列”:第一次 throw 错误,第二次返回数据
- 避免用固定 setTimeout 模拟延迟——应使用 jest.useFakeTimers() 或 sinon.clock 控制时间推进
主动推进时间并断言重试次数
重试常依赖 setTimeout 或 exponential backoff,必须让时间“快进”才能触发下一次尝试:
- Jest 中启用 fake timers:jest.useFakeTimers(),之后用 jest.runAllTimers() 或 jest.advanceTimersByTime(ms) 推进
- 在每次时间推进后,检查模拟函数的 mock.calls.length 是否符合预期(如重试 3 次 → 调用 3 次)
- 可配合 expect(fn).toHaveBeenCalledTimes(3) 直接断言调用次数
覆盖典型重试场景并检查最终状态
每个测试用例聚焦一个行为,不要混在一起:
立即学习“Java免费学习笔记(深入)”;
- 全部失败:模拟函数连续 reject(次数 ≥ maxRetries),断言最终 Promise rejected,且错误是最后一次失败的原因
- 第 N 次成功:前 N−1 次 reject,第 N 次 resolve,断言最终得到正确数据,且调用次数为 N
- 取消/中断重试(如有 AbortSignal):传入 abortController.signal,在重试中调用 abort(),验证不发起后续请求且 Promise rejected with AbortError
避免常见陷阱
实际写测试时容易忽略这些细节:
- 忘记清理 fake timers —— 测试末尾加 jest.useRealTimers() 或用 afterEach(() => jest.clearAllTimers())
- 没 await 异步断言,导致测试提前通过;所有 expect() 前确保 await 了目标 Promise
- 重试逻辑里用了 Date.now() 或 Math.random() —— 它们不受 fake timers 影响,需单独 mock(如 jest.mock('date-fns', ...) 或替换全局 Date)
- 断言错误类型时只 check message,应优先用 instanceof 判断错误构造函数(如 expect(err).toBeInstanceOf(TimeoutError))


















