Jest 测试含 fetch 和 WebSocket 的异步模块需隔离依赖、控制异步流、模拟响应:mock fetch 返回 Response,mock WebSocket 并触发事件,每次测试后清理 mock,组合测试时按序模拟依赖并验证状态流转与错误处理。

用 Jest 测试含 fetch 和 WebSocket 的异步模块,核心是**隔离外部依赖、控制异步流、模拟响应行为**。Jest 自带的 jest.mock() 和 mockImplementation() 能覆盖大部分场景,无需额外库(如 msw 或 websocket-mock)也能完成可靠测试。
模拟 fetch 请求(替代原生 window.fetch)
Jest 默认会自动 mock 全局 fetch,但需手动配置返回值。关键是:确保被测函数实际调用的是 mock 版本,并能验证请求参数与响应逻辑。
- 在测试前用
global.fetch = jest.fn()显式 mock(尤其在非浏览器环境如 Node 中) - 用
mockResolvedValue返回 Promise 包裹的 Response 对象,注意构造合法的Response(可用new Response(body, options)) - 验证请求是否按预期发起:
expect(fetch).toHaveBeenCalledWith(url, config)
示例:
test('fetches user data', async () => {const mockData = { id: 1, name: 'Alice' };
global.fetch.mockResolvedValue(new Response(JSON.stringify(mockData), { status: 200 }));
const result = await getUser(1); // 假设这是被测函数
expect(fetch).toHaveBeenCalledWith('https://api.example.com/users/1');
expect(result).toEqual(mockData);
});
模拟 WebSocket 行为(拦截 new WebSocket 构造与事件触发)
WebSocket 是事件驱动对象,不能只 mock 构造函数,还需模拟其 onopen、onmessage 等回调触发时机。常用做法是:mock WebSocket 构造函数,返回一个带可控事件触发能力的假实例。
- 用
jest.mock('ws')(Node)或global.WebSocket = jest.fn()(浏览器环境)拦截构造调用 - 让 mock 实例保存传入的
onmessage等 handler,并提供triggerMessage(data)方法主动调用它们 - 在测试中等待消息处理完成(配合
await waitFor(() => expect(...).toBe(...))或 Promise + resolve)
示例(简化版 mock):
constructor(url) {
this.url = url;
this.onopen = null;
this.onmessage = null;
}
triggerOpen() { if (this.onopen) this.onopen(); }
triggerMessage(data) { if (this.onmessage) this.onmessage({ data }); }
}
global.WebSocket = MockWebSocket;
test('handles incoming WebSocket message', async () => {
const ws = new WebSocket('ws://localhost');
ws.triggerOpen();
ws.triggerMessage('{"type":"ping"}');
// 断言业务逻辑是否响应了该消息
});
处理时序与清理(避免测试污染)
fetch 和 WebSocket 都可能引发副作用(如未关闭连接、未清除监听器),导致测试间相互干扰。务必在每个测试后重置 mock 并清理资源。
- 使用
beforeEach(() => { jest.clearAllMocks(); })清空调用记录和返回值 - 若被测模块内部创建了 WebSocket 实例,测试中应模拟其
close()行为并验证是否被调用 - 对长时间运行的异步操作(如重连逻辑),可设置
jest.setTimeout(10000)防止超时失败 - 避免在测试中使用
setTimeout等不可靠延时,优先用waitFor或直接触发事件
进阶:组合测试真实交互流程
当模块同时使用 fetch 初始化 + WebSocket 持续通信时,测试重点是**状态流转是否正确**。例如:先 fetch 获取 token,再用 token 建立 WebSocket 连接。
- 按执行顺序依次 mock fetch 返回 token,再 mock WebSocket 构造并模拟认证成功消息
- 断言最终状态(如连接标志位、缓存数据、UI 回调是否触发)
- 用
jest.runAllTimers()处理内部setInterval(如心跳检测)
关键不是“像生产一样跑通”,而是“验证每一步依赖是否被正确使用、错误路径是否被覆盖”。比如模拟 fetch 失败后是否跳过 WebSocket 连接,或 WebSocket 关闭后是否自动重试。











