javascript异步测试应避免真实延时,改用jest fake timers控制定时器;需启用模拟、推进时间、及时恢复真实计时器;手动清理定时器防泄漏;禁用真实延迟;单独mock requestanimationframe。

在 JavaScript 异步代码测试中,直接等待真实时间(比如用 setTimeout 等 1000ms)会导致测试变慢、不稳定,还容易因事件循环干扰而断言失败。核心思路是:不等真实时间,而是控制或跳过定时器的执行时机。
用 Jest 的 fake timers 模拟时间推进
Jest 内置了可靠的定时器模拟机制,能完全接管 setTimeout、setInterval 等行为:
- 调用
jest.useFakeTimers()启用模拟,之后所有定时器不再真实等待 - 用
jest.runAllTimers()立即执行所有已注册但未触发的定时器 - 用
jest.advanceTimersByTime(ms)推进指定毫秒,只触发在此区间到期的回调 - 测试结束前别忘了
jest.useRealTimers()恢复,避免影响其他测试
手动清除定时器避免内存泄漏和干扰
被测函数若启动了 setTimeout 或 setInterval,但没提供清理方式,测试中可能持续运行、重复触发,甚至报错:
- 确保被测逻辑返回定时器 ID(如
return setTimeout(...)),测试时可调用clearTimeout(id)主动释放 - 组件类中常用
componentWillUnmount或useEffect清理,测试时需模拟生命周期触发清理逻辑 - 未清理的
setInterval尤其危险——它会一直往任务队列塞回调,可能让测试套件卡死
避免在测试中依赖真实延迟
切勿写类似 await new Promise(r => setTimeout(r, 1000)) 来“等一秒”——这会让单个测试耗时至少 1 秒,积少成多拖垮整个测试套件:
- 真实延时只用于端到端(E2E)或性能压测,单元测试必须快(理想单测 ≤ 50ms)
- 若逻辑本身含防抖/节流,应验证其是否正确忽略中间调用,而非真等 300ms 后看结果
- 可将延时值抽为参数(如
debounce(fn, delay = 300)),测试时传入1或0加速验证
对 requestAnimationFrame 做特殊处理
requestAnimationFrame 不受 Jest fake timers 影响,需单独 mock:
- 在测试前 mock:
global.requestAnimationFrame = jest.fn(cb => setTimeout(cb, 0)) - 或使用社区方案如
@jest-environment-jsdom-sixteen(新版 jsdom 已支持 rAF 模拟) - 调用
jest.runOnlyPendingTimers()可确保 rAF 回调也被执行一次
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











