Jest:如何等待模拟事件触发的Mock调用?测试遇调用时机问题
Jest测试中如何等待模拟事件触发的Mock调用以确保断言准确?
我编写了如下测试用例,测试因检测到Mock未被调用而失败,但实际Mock已被调用,只是调用时机在测试流程结束前。测试代码如下:
test('should submit if proper values', () => { const spy = jest.fn().mockReturnValue({ data: { authenticateUser: { id: 123, token: 'lol' } } }); mountedWrapper.setProps({ loginMutation: spy }); expect(spy).not.toHaveBeenCalled(); email.simulate('change', { target: { name: 'username', value: 'email@test.com' } }); password.simulate('change', { target: { name: 'password', value: 'xxx' } }); // 后续提交逻辑省略 })
请问在Jest中如何等待模拟事件触发的Mock调用,以确保测试断言准确?
解决思路与方案
这种情况我太熟悉了!本质是你的提交逻辑大概率是异步操作(比如封装了Promise、用了setTimeout,或者组件内部做了异步状态更新),而Jest的测试默认是同步执行的——测试函数跑完断言的时候,异步的Mock调用还没来得及执行,所以才会出现“明明调用了但测试说没调用”的矛盾。
下面给你几个实用的解决办法:
1. 用async/await + waitFor(最推荐,通用性强)
Jest内置的waitFor方法专门用来等待异步操作完成,它会反复执行传入的断言,直到断言成功或者超时。把测试函数改成异步的,然后用waitFor包裹你的断言即可:
test('should submit if proper values', async () => { const spy = jest.fn().mockReturnValue({ data: { authenticateUser: { id: 123, token: 'lol' } } }); mountedWrapper.setProps({ loginMutation: spy }); expect(spy).not.toHaveBeenCalled(); // 输入表单值 email.simulate('change', { target: { name: 'username', value: 'email@test.com' } }); password.simulate('change', { target: { name: 'password', value: 'xxx' } }); // 触发提交操作(比如点击提交按钮) submitButton.simulate('click'); // 等待Mock被调用,直到断言成功 await waitFor(() => { expect(spy).toHaveBeenCalled(); }); })
2. 用flushPromises快速处理Promise异步
如果你的loginMutation是基于Promise的异步函数,可以用flushPromises把所有待处理的Promise立即执行完毕,不用等待自然的异步队列。
如果你的测试环境没有内置这个方法,可以自己写一个极简版:
const flushPromises = () => new Promise(setImmediate);
然后修改测试代码:
test('should submit if proper values', async () => { const spy = jest.fn().mockReturnValue({ data: { authenticateUser: { id: 123, token: 'lol' } } }); mountedWrapper.setProps({ loginMutation: spy }); expect(spy).not.toHaveBeenCalled(); email.simulate('change', { target: { name: 'username', value: 'email@test.com' } }); password.simulate('change', { target: { name: 'password', value: 'xxx' } }); // 触发提交 submitButton.simulate('click'); // 立即执行所有待处理的Promise await flushPromises(); // 现在断言就会准确了 expect(spy).toHaveBeenCalled(); })
3. 确认组件的异步逻辑触发时机
另外要注意:如果你的提交逻辑是通过setTimeout或者其他宏任务触发的,flushPromises可能不生效,这时候waitFor依然是最稳妥的选择——它会自动处理宏任务和微任务的等待。
内容的提问来源于stack exchange,提问作者StevieB
相关产品推荐
相关产品推荐

