如何在Jest中测试循环内的setTimeout与异步函数?
问题分析与解决方案
先拆解你代码里的问题:
原函数的冗余点
const arrayData = await ['123', '456', '789']; 这里没必要await普通数组,数组不是Promise,直接写成const arrayData = ['123', '456', '789'];就行,这不是测试失败的直接原因,但属于冗余代码。
测试代码的核心问题
- 断言对象错误:你写的
expect(myAsyncFunc).resolves.toBeUndefined();是错的,myAsyncFunc是函数本身,应该断言它返回的Promise,也就是之前定义的funcPromise。 - 未等待Promise完成:调用
jest.runAllTimers()推进计时器后,函数内部的await链还需要时间完成Promise的状态变更,必须等待funcPromise执行完毕再做断言。 - 断言次数不匹配:你的数组有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
相关产品推荐
相关产品推荐

