javascript单元测试中模拟请求重试失败需用jest mock替换请求函数,结合fake timers控制异步时机,断言重试次数、错误类型及最终拒绝原因,并覆盖网络中断、5xx/4xx响应等边界场景。

在 JavaScript 单元测试中模拟请求重试失败的全流程,核心是控制异步行为、触发多次失败、验证重试逻辑(如指数退避、最大重试次数)和最终错误抛出。关键不在于“真实发请求”,而在于精准接管请求调用链,让测试可预测、可断言。
用 Jest Mock 替换请求函数并控制返回行为
假设你封装了带重试的 fetch 函数(如 retryFetch(url, options)),先确保它内部调用的是可 mock 的底层方法(如全局 fetch 或自定义的 httpRequest)。在测试前用 jest.mock 或 jest.fn() 替换它,并按需返回失败响应:
- 用
mockImplementationOnce模拟连续多次 500 错误,例如:三次都返回{ ok: false, status: 500 } - 第 4 次(超出最大重试次数)可返回成功响应,或保持失败以验证最终 reject
- 若使用
fetch,记得 mockResponse和Promise.reject网络异常场景(如TypeError: Failed to fetch)
显式触发重试间隔(避免真实等待)
重试逻辑常含 setTimeout 延迟。测试中不能等几秒,要用 jest.useFakeTimers() + jest.runAllTimers() 或 jest.advanceTimersByTime(ms) 主动推进时间:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 调用被测函数后,立即
jest.runAllTimers()触发所有排队的重试尝试 - 若需验证第 1 次失败后等待 100ms、第 2 次后等待 200ms,可用
advanceTimersByTime(100)分步推进并检查中间状态 - 别忘了
beforeEach(() => jest.useFakeTimers())和afterEach(() => jest.useRealTimers())
断言重试次数、错误类型与最终拒绝原因
全流程验证不止看“是否失败”,还要确认过程符合预期:
- 用
mockFn.mock.calls.length断言请求被调用了 3 次(假设 maxRetries = 2,即首次 + 2 次重试) - 捕获最终 Promise rejection,检查是否为预期错误实例(如
instanceof RetryError)或包含重试信息的 Error message - 若重试逻辑抛出自定义错误,确保其
.attempts、.lastError等属性正确赋值
覆盖边界情况:网络中断、超时、非 5xx 响应
真实重试通常只对特定错误重试(如网络异常、5xx 服务端错误),而非 4xx 客户端错误。测试需区分:
- 模拟
Promise.reject(new TypeError('Network error'))—— 应触发重试 - 模拟返回
{ ok: false, status: 404 }—— 不应重试,立即 reject - 模拟请求超时(如用
AbortController+signal.timeout)—— 验证超时是否纳入重试判定
不复杂但容易忽略:确保被测函数本身不依赖全局定时器副作用,且所有异步路径都被 await 或返回 Promise,否则测试可能提前结束而未捕获最终错误。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










