测试异步重试函数应控制失败行为、观测调用次数、验证延迟逻辑;用 jest.fn() 模拟 fetch,按需 reject/resolve;配合 fake timers 推进时间;覆盖重试耗尽、首次成功、零重试等边界;推荐依赖注入而非 mock 全局 fetch。

测试带有重试机制的异步函数,关键不是“等它重试完”,而是**控制失败行为、观测调用次数、验证延迟逻辑**。核心在于让测试环境可预测、可观察,而不是依赖真实网络或时间流逝。
用可控 mock 模拟失败与成功
不要调用真实 fetch 或 API,而是用 jest.fn() 创建一个能按需返回 reject 或 resolve 的模拟函数:
- 让前 n 次调用抛出错误(如
Promise.reject(new Error('network error'))),第 n+1 次才 resolve 正常响应 - 确保每次 reject 都是新 Error 实例,避免 Jest mock 因引用相等误判为“同一次调用”
- 断言
mockFn.mock.calls.length === maxRetries + 1,确认首次 + 所有重试都被执行
配合 fake timers 验证延迟行为
如果重试逻辑里用了 setTimeout 或 await new Promise(r => setTimeout(r, delay)),必须启用 Jest 的假定时器:
- 测试开始前调用
jest.useFakeTimers() - 触发被测函数后,用
jest.runAllTimers()快进所有延迟,或jest.advanceTimersByTime(ms)精确推进 - 可额外断言
expect(setTimeout).toHaveBeenCalledTimes(n),验证延迟调用次数是否匹配重试次数
覆盖典型边界场景
只测“重试 3 次成功”不够,还需验证这些情况是否按预期处理:
-
重试耗尽仍失败:mock 始终 reject,断言最终抛出错误,且调用次数 =
maxRetries + 1 - 首次就成功:mock 直接 resolve,断言调用次数为 1,无任何延迟触发
-
零重试配置:传入
maxRetries = 0,应只执行一次,不进入重试分支
推荐依赖注入而非 mock 全局 fetch
直接 mock global.fetch 容易污染全局状态,也难复用于多个测试用例:
- 把请求函数(如 fetch)作为参数传入,例如:
fetchWithRetry(url, { fetch: mockFetch, maxRetries: 2 }) - 测试时完全掌控传入的
mockFetch行为,无需 monkey patch - 更利于单元隔离,也方便在不同策略下复用同一套测试逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











