Jest 的 fake timers 可跳过真实等待,通过 jest.useFakeTimers() 全局或局部启用,配合 jest.advanceTimersByTime() 等推进虚拟时间,精准测试异步延时逻辑。

Jest 的 fake timers 能让定时器(setTimeout、setInterval、Date.now() 等)不真实等待,直接“快进”到指定时间,非常适合测试异步延时逻辑。
启用 fake timers 的三种方式
根据测试需求选择合适的方式:
-
全局启用(推荐):在测试文件顶部加
jest.useFakeTimers();,所有后续定时器都会被拦截;测试结束后记得调用jest.useRealTimers();清理(或用afterEach统一恢复) -
仅当前测试启用:在
test或it内部调用jest.useFakeTimers();,作用域更安全,无需手动恢复(Jest 会在测试结束自动还原) -
只 mock 时间(不拦截定时器):用
jest.setSystemTime(timestamp)固定Date.now()返回值,适合验证时间戳逻辑,不影响setTimeout
跳过等待的核心方法
fake timers 启用后,不再靠真实秒数等待,而是用以下方法主动推进时间:
-
jest.runAllTimers():立即执行所有已排队但未触发的定时器(包括已到期和未来到期的),适合“等全部完成”场景 -
jest.advanceTimersByTime(ms):把虚拟时钟向前拨动ms毫秒,期间到期的定时器会被依次执行(支持多次调用,模拟分段等待) -
jest.runOnlyPendingTimers():只执行当前已到期、尚未执行的定时器(不处理未来定时器),适合“只走一步”的精确控制
实战示例:测试一个带延迟的重试函数
比如有个函数每 1000ms 重试一次,最多 3 次:
function retryWithDelay(fn, max = 3) {
let count = 0;
const attempt = () => {
if (count >= max) return Promise.reject('failed');
count++;
return fn().catch(() => {
setTimeout(attempt, 1000);
});
};
return attempt();
}
用 fake timers 测试它是否按预期重试:
test('retries exactly 3 times with 1s delay', () => {
jest.useFakeTimers();
const mockFn = jest.fn().mockRejectedValueOnce('err1')
.mockRejectedValueOnce('err2')
.mockResolvedValue('success');
retryWithDelay(mockFn);
// 第一次失败 → 排队第二次(1000ms 后)
expect(mockFn).toHaveBeenCalledTimes(1);
// 快进 1000ms,触发第二次调用
jest.advanceTimersByTime(1000);
expect(mockFn).toHaveBeenCalledTimes(2);
// 再快进 1000ms,触发第三次
jest.advanceTimersByTime(1000);
expect(mockFn).toHaveBeenCalledTimes(3);
// 再快进 1000ms,应不再调用(已成功)
jest.advanceTimersByTime(1000);
expect(mockFn).toHaveBeenCalledTimes(3);
});
注意事项和常见坑
fake timers 很强大,但也容易踩坑:
- 确保在
jest.useFakeTimers()后再创建定时器,否则它们仍走真实时间 - 若测试中用了
Promise.resolve().then(...)这类微任务,fake timers 不影响它们——需搭配await Promise.resolve()或await flushPromises()(自定义辅助函数)来清空微任务队列 - 避免在测试中混用
setTimeout和setImmediate/process.nextTick(Node.js),后者不受 fake timers 控制 - 使用
jest.runAllTimers()前确认没有无限定时器(如没清理的setInterval),否则会卡死











