最常用且可靠的方式是使用 jest 内置的定时器模拟功能(jest.usefaketimers()),它能拦截 settimeout、setinterval 等原生定时器,并提供 jest.runalltimers()、jest.advancetimersbytime(ms)、jest.runonlypendingtimers() 等精确控制方法;推荐在 beforeeach 中启用、aftereach 中恢复真实定时器以确保测试隔离。

在 JavaScript 测试中手动快进时间执行定时器,最常用且可靠的方式是使用 Jest 内置的定时器模拟功能(jest.useFakeTimers()),它能拦截 setTimeout、setInterval 等原生定时器,并提供精确控制时间推进的能力。
启用假定时器并触发待执行的回调
调用 jest.useFakeTimers() 后,所有后续的 setTimeout 不会真实等待,而是被收集到内部队列。你可以用以下方法立即执行已到期或排队中的回调:
-
jest.runAllTimers():执行所有已设定、且“时间已到”的定时器(包括已过期和即将过期的) -
jest.advanceTimersByTime(ms):将虚拟时间向前推进指定毫秒数,然后执行在此期间到期的所有定时器 -
jest.runOnlyPendingTimers():只执行当前已排队、尚未执行的定时器(不推进时间)
典型测试场景示例
比如你有一个函数,300ms 后调用回调:
测试它是否被正确调用:
- 先调用
jest.useFakeTimers() - 执行
delayCallback(mockCb) - 调用
jest.advanceTimersByTime(300)—— 此时mockCb会被立即执行 - 断言
expect(mockCb).toHaveBeenCalledTimes(1)
注意清理与重置
为避免定时器状态污染其他测试,推荐在每个测试前后做隔离处理:
- 在
beforeEach中调用jest.useFakeTimers() - 在
afterEach中调用jest.useRealTimers()恢复真实行为 - 也可在单个测试末尾用
jest.clearAllTimers()清空待执行队列
不使用 Jest?可考虑 sinon.js 或手动 mock
若项目用的是 Mocha、Vitest 等其他框架:
-
Vitest:支持
vi.useFakeTimers(),API 与 Jest 高度兼容 -
sinon.js:提供
sinon.useFakeTimers(),配合clock.tick(ms)推进时间 - 纯手工 mock:重写全局
setTimeout,但需自行管理队列和执行逻辑,易出错,不推荐
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











