Jest中waitFor为何无法等待setTimeout?延长超时时间也无效
1. Jest定时器模式的核心差异
Jest有两种定时器处理模式,这是导致你遇到差异的根本原因:
假定时器模式(通过
jest.useFakeTimers()启用):
Jest会拦截所有setTimeout/setInterval调用,不会让时间真实流逝,所有定时器任务都会被挂起,不会自动执行。waitFor本质是定期轮询断言,但假定时器不推进的话,你的2秒延迟回调永远不会触发——不管waitFor设置多长超时,都等不到状态变更,必须手动调用jest.advanceTimersByTime(2000)或jest.runAllTimers(),才能触发回调并推进React状态更新,此时waitFor才能捕获到变化。
而20ms延迟能工作,大概率是你测试这段逻辑时没启用假定时器(或局部禁用了),真实定时器的20ms延迟远小于waitFor的默认超时(1000ms),在waitFor的轮询周期内,回调已经执行并完成了React状态更新,所以断言能通过。真实定时器模式(默认):
理论上只要把waitFor的超时时间设得比2000ms长(比如waitFor(..., { timeout: 3000 })),应该能等到状态变更,但实际测试中Jest可能因为性能限制,不会让测试真的等待这么久;另外很多开发者会习惯性在测试中启用假定时器,这也会导致真实时间不流逝,间接引发等待失效。
2. waitFor的轮询机制限制
waitFor默认每隔50ms轮询一次断言是否成立,默认超时时间是1000ms:
- 20ms延迟时,在第一个轮询周期(50ms内),定时器回调已经执行、状态已更新,所以
waitFor能立刻检测到变化。 - 2000ms延迟时,即使使用真实定时器,若
waitFor没设置足够长的超时(比如还是默认1000ms),会在回调触发前就超时失败;就算设置了更长超时,Jest测试环境也会因为不想让测试耗时太久,间接导致等待失效——这也是官方推荐测试定时器时用假定时器+手动推进的原因。
总结
你遇到的现象本质是Jest定时器模式与waitFor工作机制的冲突:启用假定时器时,必须手动推进才能触发定时器回调,waitFor无法自动等待假定时器的任务;20ms延迟能工作,是因为真实定时器下,回调在waitFor默认超时和轮询周期内完成了执行。
内容的提问来源于stack exchange,提问作者Hao

