Vitest 内置假定时器支持,通过 vi.useFakeTimers() 启用并配合 vi.runAllTimers() 或 vi.advanceTimersByTime() 控制时间推进,可高效测试异步逻辑、防抖节流等场景。

在 Vitest 中测试异步代码(尤其是涉及 setTimeout、setInterval、Promise 等定时逻辑)时,使用假定时器(Fake Timers)能避免真实等待、提升测试速度和确定性。Vitest 内置了基于 @sinonjs/fake-timers 的假定时器支持,无需额外安装依赖。
启用假定时器
在测试文件顶部或 beforeEach 中调用 vi.useFakeTimers() 即可启用:
- 它会拦截全局的
setTimeout、setInterval、clearTimeout、clearInterval、Date等 API - 推荐在
beforeEach中启用,在afterEach中恢复,确保测试隔离
import { vi, it, beforeEach, afterEach } from 'vitest'
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.restoreAllMocks() // 恢复所有 mock,包括假定时器
})
控制时间推进
启用后,定时器不会真实执行,需手动“快进”时间才能触发回调:
-
vi.runAllTimers():立即执行所有已排队的定时器回调(包括已到期和未来到期的) -
vi.advanceTimersByTime(ms):将虚拟时间向前推进指定毫秒数,只触发在此期间到期的回调 -
vi.advanceTimersToNextTimer():只推进到下一个最近的定时器到期点并执行它
await new Promise(resolve => setTimeout(resolve, 100)),必须调用 vi.runAllTimers() 或 vi.advanceTimersByTime(100) 才能让 Promise resolve。
测试带延迟的函数(如防抖/节流)
假定时器特别适合验证防抖(debounce)、节流(throttle)等逻辑:
立即学习“Java免费学习笔记(深入)”;
- 调用被测函数多次
- 用
vi.advanceTimersByTime(…)推进到防抖窗口末尾 - 断言回调是否按预期执行一次(而非多次)
it('calls callback once after delay', () => {
const callback = vi.fn()
const debounced = debounce(callback, 100)
debounced()
debounced()
debounced()
vi.advanceTimersByTime(50) // 还没到 100ms,不执行
expect(callback).not.toHaveBeenCalled()
vi.advanceTimersByTime(50) // 到达 100ms,执行一次
expect(callback).toHaveBeenCalledTimes(1)
})
注意事项与常见陷阱
-
vi.restoreAllMocks()会自动还原假定时器;若单独调用vi.useRealTimers(),也需配对使用,否则可能影响后续测试 - 假定时器默认不模拟
requestAnimationFrame,如需支持,需显式传入选项:vi.useFakeTimers({ toFake: ['setTimeout', 'setInterval', 'requestAnimationFrame'] }) - 若测试中混用
Promise.resolve().then(…)和定时器,注意微任务(microtask)仍会立即执行,假定时器只影响宏任务(macrotask)


















