单元测试中模拟javascript请求取消的关键是控制异步生命周期以真实触发取消逻辑,包括使用abortcontroller模拟fetch取消、mock自定义请求函数返回可取消promise、验证副作用清理,并避免提前abort、环境不支持或faketimers干扰等陷阱。

在单元测试中模拟 JavaScript 请求取消,关键在于控制异步操作的生命周期,让被测代码能真实触发取消逻辑(如 AbortController 的 abort() 或 Promise 拒绝)。下面分几种常见场景给出可落地的模拟方法。
使用 AbortController 模拟 fetch 取消
现代 fetch 支持 signal 选项,单元测试时可手动创建 AbortController 实例,在断言前调用 abort():
- 在测试 setup 阶段创建
const controller = new AbortController() - 调用被测函数时传入
{ signal: controller.signal } - 立即或稍后调用
controller.abort()(注意:必须在 fetch 发起后、响应返回前) - 断言函数是否抛出
AbortError,或是否正确处理了取消(如清理副作用、不更新状态)
示例(Jest):
test('fetch 被取消时不应更新 state', async () => {const controller = new AbortController();
const mockFetch = jest.fn(() =>
new Promise((_, reject) => setTimeout(() => reject(new DOMException('aborted', 'AbortError')), 10))
);
global.fetch = mockFetch;
await expect(fetchData({ signal: controller.signal })).rejects.toThrow('aborted');
controller.abort(); // 确保 abort 在 fetch 过程中触发
});
用 jest.mock 模拟返回可取消 Promise 的函数
若封装了自定义请求函数(如 api.get('/user')),可在测试中 mock 它返回一个带 cancel 方法的 Promise,或直接 resolve/reject 控制流程:
- mock 函数返回一个手动构造的 Promise,并暴露
resolve/reject引用 - 调用被测函数后,主动调用
reject(new Error('canceled'))模拟取消 - 适用于 axios、自定义 request 库等不原生支持 AbortController 的场景
验证取消后的副作用清理
取消不仅是中断网络请求,还常涉及清理定时器、取消未完成的 setState、关闭 WebSocket 等。测试时需验证这些行为是否执行:
- 用
jest.spyOn(global, 'clearTimeout')或jest.spyOn(React, 'useEffect')(需配合 act)捕获清理动作 - 检查组件是否跳过后续状态更新(如用
act+waitFor确认 state 未改变) - 对 useEffect 中的 cleanup 函数,可直接调用并断言其内部逻辑
避免常见陷阱
模拟取消容易因时机或环境问题失败:
- 不要在
beforeEach中提前abort()—— 此时 fetch 尚未发起,signal 无效 - Jest 默认不支持
AbortController(旧版 Node),需确保运行在 Node ≥15 或启用--experimental-abortcontroller - 使用
fakeTimers时注意:setTimeout和AbortSignal的交互可能被拦截,建议用runOnlyPendingTimers替代advanceTimersByTime
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











