4

我从只运行 axios GET 切换到返回一个承诺,现在我的 Jest 测试失败了:

在“resource.js”中下载 zip:

async function downloadMtgJsonZip() {
  const path = Path.resolve(__dirname, 'resources', fileName);
  const writer = Fs.createWriteStream(path);

  console.info('...connecting...');
  const { data, headers } = await axios({
    url,
    method: 'GET',
    responseType: 'stream',
  });
  return new Promise((resolve, reject) => {
    let error = null;
    const totalLength = headers['content-length'];
    const progressBar = getProgressBar(totalLength);
    console.info('...starting download...');
    data.on('data', (chunk) => progressBar.tick(chunk.length));
    data.pipe(writer);
    writer.on('error', (err) => {
      error = err;
      writer.close();
      reject(err);
    });
    writer.on('close', () => {
      const now = new Date();
      console.info(`Completed in ${(now.getTime() - progressBar.start) / 1000} seconds`);
      if (!error) resolve(true);
      // no need to call the reject here, as it will have been called in the
      // 'error' stream;
    });
  });
}

'resource.spec.js' 中的以下测试现在都没有通过:

it('fetches successfully data from an URL', async () => {
    const onFn = jest.fn();
    const data = { status: 200, data: { pipe: () => 'data', on: onFn }, headers: { 'content-length': 100 } };

    const writerOnFn = jest.fn();

    axios.mockImplementationOnce(() => data);
    fs.createWriteStream.mockImplementationOnce(() => ({ on: writerOnFn }));
    await downloadMtgJsonZip();
    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();
    const dataOnFn = jest.fn((name, func) => func(['chunk']));
    const data = { status: 200, data: { pipe: () => 'data', on: dataOnFn }, headers: { 'content-length': 1 } };

    const writerOnFn = jest.fn();

    ProgressBar.mockImplementationOnce(() => ({ tick: tickFn }));
    axios.mockImplementationOnce(() => data);
    fs.createWriteStream.mockImplementationOnce(() => ({ on: writerOnFn }));
    await downloadMtgJsonZip();

    expect(ProgressBar).toHaveBeenCalledWith(
      expect.stringContaining('downloading'),
      expect.objectContaining({
        total: 1,
      }),
    );
    expect(tickFn).toHaveBeenCalledWith(1);
  });
});

值得注意的是,VSCode 告诉我,对于axios'resource.js','这个表达式不可调用'并且什么都没有mockImplementationOnce(它'不存在于类型......')。

以前我的downloadMtgJsonZip样子是这样的:

async function downloadMtgJsonZip() {
  const path = Path.resolve(__dirname, 'resources', 'AllPrintings.json.zip');
  const writer = Fs.createWriteStream(path);

  console.info('...connecting...');
  const { data, headers } = await axios({
    url,
    method: 'GET',
    responseType: 'stream',
  });
  const totalLength = headers['content-length'];
  const progressBar = getProgressBar(totalLength);
  const timer = setInterval(() => {
    if (progressBar.complete) {
      const now = new Date();
      console.info(`Completed in ${(now.getTime() - progressBar.start) / 1000} seconds`);
      clearInterval(timer);
    }
  }, 100);
  console.info('...starting download...');
  data.on('data', (chunk) => progressBar.tick(chunk.length));
  data.pipe(writer);
}

并且测试中唯一不同的行是 createWriteStream 的模拟更简单(它读取fs.createWriteStream.mockImplementationOnce(() => 'fs');

我试过添加:

  afterEach(() => { 
    jest.clearAllMocks(); 
    jest.resetAllMocks();
  });

我已经尝试添加writerOnFn('close');以尝试writer.on('close', ...)触发。

但我直到收到这个错误:

:超时 - 在 jest.setTimeout.Timeout 指定的 5000 毫秒超时内未调用异步回调 - 在 jest.setTimeout.Error 指定的 5000 毫秒超时内未调用异步回调:

我无法弄清楚缺少什么,以使异步调用被“调用”。上次我有这个问题模拟解决了createWriteStream我的问题,但我没有看到任何其他可以模拟的东西?

如何让这些测试再次通过?

4

2 回答 2

2

如何writer.on(event, handler)在测试代码中调用使用 get 附加的事件处理程序?writerOnFn模拟不需要调用传入的处理函数吗?如果那些没有被调用,那么resolve(true)就永远不会被调用,因此对await downloadMtgJsonZip();测试内部的调用永远不会解决。

我认为你需要这样的东西

const writerOnFn = jest.fn((e, cb) => if (e === 'close') cb())

当然,您可能想要充实它以区分“错误”和“关闭”事件,或者如果您有围绕“错误”条件的测试,请确保更改它。

于 2021-10-06T00:58:43.467 回答
-1

Jest 完成异步测试的默认超时时间为 5000 毫秒(参考:https ://jestjs.io/docs/configuration#testtimeout-number )

如果您长时间运行异步调用,则有必要增加此阈值。

例如在我jest.config.js的超时设置为 60000ms

module.exports = {
  ...
  testTimeout: 60000, 
}
于 2021-10-01T20:01:14.233 回答