单元测试 JavaScript 请求重试逻辑需控制 fetch 行为并捕获调用次数与参数:用 mockImplementation 模拟失败/成功响应序列,验证调用总次数为 N+1;区分错误类型确保仅对网络错误/5xx 重试;隔离 setTimeout 和 AbortController 等副作用;检查每次请求参数一致性,并 await 异步函数覆盖所有路径。

在单元测试中验证 JavaScript 请求重试逻辑的准确次数,核心是**控制依赖(如 fetch)的行为,并捕获每次调用的时机与参数**。关键不在于“测重试本身是否发生”,而在于“当失败 N 次后成功,函数是否恰好调用了 N+1 次请求”。
用 Mock 控制 fetch 返回值序列
借助 Jest 或 Vitest 的 mockImplementation,可让每次 fetch 调用返回不同响应(例如前两次 500,第三次 200)。这样能精确模拟重试场景:
- 定义一个计数器或数组队列,按顺序返回预设响应(如
[Promise.reject(new Error('timeout')), Promise.reject(new Error('500')), Promise.resolve({ ok: true })]) - 确保被测函数内部调用的是你 mock 的
fetch(可通过模块 mock 或传入 fetch 函数作为参数来解耦) - 执行被测函数后,检查
fetch.mock.calls.length是否等于预期重试总次数(如重试 2 次 → 共调用 3 次)
验证重试间隔与错误类型是否匹配策略
仅看调用次数不够,还要确认重试是否按设计触发(比如只对网络错误/5xx 重试,不对 400 或解析错误重试):
- 分别 mock 不同响应:返回
{ status: 503 }、{ status: 400 }、throw new TypeError(),验证只有前两者触发重试(调用次数 > 1),后者只调用 1 次 - 若重试含延迟(如
setTimeout),可用jest.useFakeTimers()控制时间推进,再结合jest.runOnlyPendingTimers()触发下一次重试
避免副作用干扰:隔离定时器与全局状态
真实重试常依赖 setTimeout 或 AbortController,测试时需隔离:
立即学习“Java免费学习笔记(深入)”;
- 将延迟逻辑抽离为可注入函数(如
delayMs = (ms) => new Promise(r => setTimeout(r, ms))),测试时 mock 它立即 resolve - 若使用
AbortController,mock 其构造函数和abort()行为,防止测试因超时中断 - 确保每次测试前清理 mock 状态:
fetch.mockClear()、jest.clearAllMocks()
断言具体调用参数与顺序
除了次数,还可验证每次请求是否携带正确参数(如 headers、body、URL),尤其重试时是否保留原始配置:
- 检查
fetch.mock.calls[i][0](URL)和fetch.mock.calls[i][1].headers是否一致 - 若重试带指数退避,可 mock
Date.now()或记录每次调用时间戳,验证间隔是否符合预期(如 100ms → 200ms) - 用
expect(fetch).toHaveBeenNthCalledWith(1, '/api', {...})显式断言第 N 次调用细节
不复杂但容易忽略:重试逻辑往往嵌套在异步链中(如 async/await + try/catch + 循环),测试时务必 await 被测函数,并用 done() 或 rejects.toThrow() 覆盖最终失败路径。


















