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

如何在Jest中测试循环内的setTimeout与异步函数?

问题分析与解决方案

先拆解你代码里的问题:

原函数的冗余点

const arrayData = await ['123', '456', '789']; 这里没必要await普通数组,数组不是Promise,直接写成const arrayData = ['123', '456', '789'];就行,这不是测试失败的直接原因,但属于冗余代码。

测试代码的核心问题

  1. 断言对象错误:你写的expect(myAsyncFunc).resolves.toBeUndefined();是错的,myAsyncFunc是函数本身,应该断言它返回的Promise,也就是之前定义的funcPromise。
  2. 未等待Promise完成:调用jest.runAllTimers()推进计时器后,函数内部的await链还需要时间完成Promise的状态变更,必须等待funcPromise执行完毕再做断言。
  3. 断言次数不匹配:你的数组有3个元素,doAsyncThing和setTimeout应该被调用3次,而不是测试里写的2次。

修正后的测试代码

jest.mock('../../../somewhere');
jest.useFakeTimers();

test('a test', async () => {
  const funcPromise = myAsyncFunc();
  // 推进所有假计时器
  jest.runAllTimers();
  // 等待函数执行完成
  await funcPromise;

  // 断言函数返回值
  expect(funcPromise).resolves.toBeUndefined();
  // 数组有3个元素,对应3次调用
  expect(doAsyncThing).toBeCalledTimes(3);
  expect(setTimeout).toBeCalledTimes(3);
  // 可选:验证每次调用的参数是否正确
  expect(doAsyncThing).toHaveBeenNthCalledWith(1, '123');
  expect(doAsyncThing).toHaveBeenNthCalledWith(2, '456');
  expect(doAsyncThing).toHaveBeenNthCalledWith(3, '789');
});

为什么这样能生效?

  • 调用myAsyncFunc()后,函数开始执行,第一个await doAsyncThing(item)会立即resolve(jest mock函数默认返回resolved Promise),随后进入await new Promise(...)创建假计时器。
  • jest.runAllTimers()会触发所有pending的setTimeout,让等待1秒的Promise resolve,循环继续执行下一次迭代。
  • await funcPromise等待整个函数的Promise链完成,此时所有doAsyncThing调用都已执行完毕,断言就不会再出现调用次数为0的问题。

内容的提问来源于stack exchange,提问作者Colin Cheung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:17:31