Jest单元测试中setTimeout()无法正常工作问题求助
Vue.js项目Jest单元测试:setTimeout逻辑中changeRoute未触发问题
用Jest对Vue.js项目进行单元测试时,测试setTimeout相关逻辑遇到问题:调用jest.advanceTimersByTime()推进指定时长后,changeRoute方法仍未被触发。
额外信息
- 框架:Vue.js
- 测试库:Jest
已尝试操作
- 在设置任何定时器前调用
jest.useFakeTimers() - 确认setTimeout的spy已正确配置
- 确保在推进定时器前等待
submitCall执行
相关代码
业务代码
async submitCall(error, data, message) { if (isBlank(error)) { this.successMessage = message; if (isBlank(data)) { await this.loadData(this.currentReportId); if (hasValue(this.successMessage)) { await new Promise((resolve) => { setTimeout(async () => { this.changeRoute({ name: 'report' }); resolve(); }, 5000); }); } } else { await this.pageRedirection(data); } } }
测试用例(原版本)
it('should call changeRoute after 5 seconds', async () => { jest.useFakeTimers(); jest.spyOn(window, 'setTimeout'); wrapper.vm.changeRoute = jest.fn(); wrapper.vm.pageRedirection = jest.fn(); expect(wrapper.vm.changeRoute).not.toHaveBeenCalled(); const submitCallPromise = wrapper.vm.submitCall('', '', 'success message'); jest.advanceTimersByTime(5000); await submitCallPromise; expect(wrapper.vm.changeRoute).toHaveBeenCalledTimes(1); });
报错信息
模块聚合错误:断言失败,期望changeRoute方法被调用1次,但实际调用次数为0。
问题原因及解决方案
问题根源
loadData未被mock:原代码中await this.loadData()如果是真实异步操作,会阻塞测试流程,导致后续定时器逻辑无法按预期执行。- 假定时器推进后未处理微任务:Jest假定时器推进后,
setTimeout回调会进入任务队列,但需手动触发微任务处理,才能让resolve()执行,进而完成await new Promise的等待。
修改后的测试用例
// 测试结束后恢复真实定时器,避免影响其他测试 afterEach(() => { jest.useRealTimers(); }); it('should call changeRoute after 5 seconds', async () => { jest.useFakeTimers(); // Mock loadData,规避真实异步操作干扰 wrapper.vm.loadData = jest.fn().mockResolvedValue(); wrapper.vm.changeRoute = jest.fn(); wrapper.vm.pageRedirection = jest.fn(); expect(wrapper.vm.changeRoute).not.toHaveBeenCalled(); // 调用submitCall,暂不await const submitCallPromise = wrapper.vm.submitCall('', '', 'success message'); // 推进定时器到5秒后,触发setTimeout回调 jest.advanceTimersByTime(5000); // 处理所有待处理微任务,让Promise的resolve执行 await Promise.resolve(); // 等待submitCall所有异步逻辑完成 await submitCallPromise; expect(wrapper.vm.changeRoute).toHaveBeenCalledTimes(1); });
额外优化点
- 移除不必要的
jest.spyOn(window, 'setTimeout'):若无需断言setTimeout的调用情况,可省略该spy,不影响假定时器功能。 - 去掉
setTimeout回调的async标记:如果changeRoute是同步方法,回调无需标记为async,避免不必要的异步嵌套。
内容的提问来源于stack exchange,提问作者Jay Patel
相关产品推荐
相关产品推荐

