js定时器单元测试需用jest fake timers冻结真实时间,通过jest.usefaketimers()接管定时器,配合jest.advancetimersbytime()等推进虚拟时间,并mock date.now()确保时间一致性,覆盖防抖、节流、轮询、超时等场景。

将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
JS 项目中定时器代码(如 setTimeout、setInterval)的单元测试,关键在于**不让真实时间流逝**,否则测试会变慢、不可控、难断言。Jest 提供了成熟可靠的 fake timers 机制,配合合理的时间推进策略,就能让定时器逻辑可预测、可验证。
用 jest.useFakeTimers() 拦截并控制定时器
这是最核心的一步。调用后,所有 setTimeout、setInterval 等原生 API 不再触发真实延时,而是被 Jest 虚拟时钟接管:
- 在
beforeEach中启用:jest.useFakeTimers(); - 在
afterEach中恢复真实计时器:jest.useRealTimers();,避免污染其他测试 - 启用后,定时器回调不会自动执行,必须显式“拨动时间”或“运行待处理任务”
根据场景选择合适的时间推进方式
不能只靠 jest.runAllTimers() 一招打天下,要按逻辑意图选:
- 想验证“延迟 100ms 后执行某操作” → 用
jest.advanceTimersByTime(100),然后检查状态或 mock 是否被调用 - 想验证“多次重复执行”(如轮询)→ 先
advanceTimersByTime(100)触发第一次,再advanceTimersByTime(100)触发第二次,逐次断言 - 想验证“立即清掉未执行的定时器” → 用
jest.runOnlyPendingTimers()配合clearTimeout断言,防止无限循环风险
模拟时间戳以保持上下文一致
如果定时器逻辑里还依赖 Date.now() 或 new Date()(比如记录触发时间、计算耗时),必须一并 Mock,否则虚拟时间和真实时间戳对不上:
- 固定
Date.now:Date.now = jest.fn(() => 1728820800000);(对应 2024-10-15) - 更通用的方式是重写
global.Date构造函数,让它在无参调用时返回预设时间 - 记得在
afterEach中还原原始Date,避免影响后续测试
测试常见模式:防抖、节流、轮询、超时控制
这些典型场景有固定结构,测试时抓住关键点即可:
-
防抖(debounce):快速连续调用函数,只检查最后一次调用后 delay 时间内是否执行;用
advanceTimersByTime(delay - 1)确保未触发,再advanceTimersByTime(1)触发 -
节流(throttle):验证首次调用立即执行,之后每 delay 时间最多执行一次;可用
runOnlyPendingTimers()+ 多次advanceTimersByTime(delay)组合断言 - 轮询(polling):启动后检查是否注册了定时器,再推进时间验证回调执行次数和参数
- 超时逻辑:模拟异步操作失败,验证超时回调是否在指定时间后被调用,且原 Promise 被 reject










