You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jest测试返回Promise的axios流方法时出现超时错误如何解决

问题原因

你的downloadMtgJsonZip方法调整后返回了一个自定义Promise,该Promise仅在writer触发close/error事件时才会resolve/reject。你的测试用例中存在两个核心问题导致Promise永远不会被解决,最终触发超时:

  1. mock的axios返回的是普通对象,而非异步Promise,await axios()之后拿到的值不符合预期
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 04:15:02