Jest测试返回Promise的axios流方法时出现超时错误如何解决
问题原因
你的downloadMtgJsonZip方法调整后返回了一个自定义Promise,该Promise仅在writer触发close/error事件时才会resolve/reject。你的测试用例中存在两个核心问题导致Promise永远不会被解决,最终触发超时:
- mock的axios返回的是普通对象,而非异步Promise,
await axios()之后拿到的值不符合预期 - mock的
createWriteStream返回的on方法仅做了函数记录,没有触发你注册的close事件回调,导致返回的Promise一直处于pending状态
修复方案
1. 基础依赖mock声明
首先确认测试文件顶部已经对依赖做了全局mock:
jest.mock('axios'); jest.mock('fs'); jest.mock('progress', () => jest.fn()); // 如果你用的是progress库的话
2. 修正测试用例逻辑
第一个测试修改后代码:
it('fetches successfully data from an URL', async () => { const onFn = jest.fn(); // axios返回的是resolve后的响应对象 const response = { status: 200, data: { pipe: jest.fn(), on: onFn }, headers: { 'content-length': 100 } }; // 存储writer的事件回调 const writerEventHandlers = {}; const writerOnFn = jest.fn((event, callback) => { writerEventHandlers[event] = callback; }); // 注意这里返回Promise.resolve,符合axios异步调用的逻辑 axios.mockImplementationOnce(() => Promise.resolve(response)); fs.createWriteStream.mockImplementationOnce(() => ({ on: writerOnFn, close: jest.fn() })); // 先拿到Promise引用,手动触发close事件后再await const downloadPromise = downloadMtgJsonZip(); writerEventHandlers['close'](); await downloadPromise; expect(onFn).toHaveBeenCalledWith('data', expect.any(Function)); expect(axios).toHaveBeenCalledWith( expect.objectContaining({ url: 'https://mtgjson.com/api/v5/AllPrintings.json.zip' }), ); expect(axios).toHaveBeenCalledWith( expect.objectContaining({ responseType: 'stream' }), ); });
第二个测试修改后代码:
it('ticks up the progress bar', async () => { const tickFn = jest.fn(); // 传入的chunk长度为1,匹配测试预期 const dataOnFn = jest.fn((name, func) => func({ length: 1 })); const response = { status: 200, data: { pipe: jest.fn(), on: dataOnFn }, headers: { 'content-length': 1 } }; const writerEventHandlers = {}; const writerOnFn = jest.fn((event, callback) => { writerEventHandlers[event] = callback; }); ProgressBar.mockImplementationOnce(() => ({ tick: tickFn })); axios.mockImplementationOnce(() => Promise.resolve(response)); fs.createWriteStream.mockImplementationOnce(() => ({ on: writerOnFn, close: jest.fn() })); const downloadPromise = downloadMtgJsonZip(); writerEventHandlers['close'](); await downloadPromise; expect(ProgressBar).toHaveBeenCalledWith( expect.stringContaining('downloading'), expect.objectContaining({ total: 1, }), ); expect(tickFn).toHaveBeenCalledWith(1); });
3. 解决VSCode类型报错
出现「表达式不可调用」和「mockImplementationOnce不存在」的提示是类型问题,按以下步骤修复:
- 确认axios的导入方式为默认导入:
import axios from 'axios',不要用import * as axios from 'axios' - 给mock的axios做类型断言,示例:
import axios from 'axios'; const mockedAxios = axios as jest.MockedFunction<typeof axios>; // 后续调用mock方法用mockedAxios即可 mockedAxios.mockImplementationOnce(() => Promise.resolve(response));
内容的提问来源于stack exchange,提问作者AncientSwordRage
相关产品推荐
相关产品推荐

