javascript单元测试中mock时间戳和定时器的核心目标是让时间可控、可预测、可跳转,避免测试变慢或不稳定;jest通过jest.usefaketimers()控制定时器,配合jest.advancetimersbytime()等api推进虚拟时间,并需手动mock date.now()或global.date以统一时间戳。

在 JavaScript 单元测试中,Mock 时间戳和定时器函数(setTimeout、setInterval、Date.now() 等)的核心目标是:**让时间可控、可预测、可跳转,避免测试因真实时间流逝而变慢或不稳定。** Jest 提供了开箱即用的 jest.useFakeTimers() 和相关 API,是最常用也最推荐的方式。
用 jest.useFakeTimers() 控制定时器行为
这是 Jest 内置的“假定时器”机制,会拦截所有原生定时器调用(setTimeout、setInterval、clearTimeout、clearInterval),并提供手动推进时间的能力。
-
启用假定时器:在测试前调用
jest.useFakeTimers();通常放在beforeEach中 -
触发已注册的定时器:
-
jest.runAllTimers():立即执行所有待执行的定时器回调(适合无延迟或固定延迟场景) -
jest.advanceTimersByTime(ms):将虚拟时钟向前拨动指定毫秒数,期间触发所有到期的定时器 -
jest.runOnlyPendingTimers():只执行当前已到期、尚未执行的那一批(防止无限循环)
-
-
清理与重置:测试后建议调用
jest.useRealTimers()或在afterEach中恢复,避免影响其他测试
模拟 Date.now() 和 new Date()
Jest 的 fake timers 默认不接管 Date 构造函数或 Date.now()。如需控制时间戳,需手动 Mock:
-
Mock
Date.now():const originalNow = Date.now; beforeEach(() => { Date.now = jest.fn(() => 1717027200000); // 固定返回 2024-05-30 00:00:00 }); afterEach(() => { Date.now = originalNow; }); -
Mock
new Date()(更通用):const originalDate = global.Date; beforeEach(() => { global.Date = class extends originalDate { constructor(...args) { if (args.length === 0) { super('2024-05-30T00:00:00.000Z'); } else { super(...args); } } }; }); afterEach(() => { global.Date = originalDate; });
结合使用:验证带延时和时间戳逻辑的函数
例如一个防抖函数或带“最后更新时间”的状态管理器:
function fetchWithTimestamp() {
const start = Date.now();
return new Promise(resolve => {
setTimeout(() => {
resolve({ data: 'ok', timestamp: Date.now(), duration: Date.now() - start });
}, 1000);
});
}
<p>// 测试
test('fetchWithTimestamp returns correct duration and timestamp', () => {
jest.useFakeTimers();
const originalNow = Date.now;
Date.now = jest.fn().mockReturnValue(1000);</p><p>let result;
fetchWithTimestamp().then(r => result = r);</p><p>expect(result).toBeUndefined();</p><p>// 推进 1000ms,触发 setTimeout 回调
jest.advanceTimersByTime(1000);</p><p>expect(result).toEqual({
data: 'ok',
timestamp: 2000, // 1000 + 1000
duration: 1000
});</p><p>Date.now = originalNow;
jest.useRealTimers();
});</p>
注意事项与替代方案
注意:Jest 的 fake timers 在异步测试中需确保等待 Promise 完成(如使用 await 或 return Promise),否则可能断言过早;同时避免在 runAllTimers() 后再注册新定时器却未推进时间,导致遗漏。
- 若项目未用 Jest,可用
sinon.useFakeTimers()(Sinon.js)或轻量库如@sinonjs/fake-timers - 对 Node.js 环境,也可通过
globalThis.setTimeout = ...手动替换,但维护成本高、易出错,不推荐 - 真实时间依赖强的集成测试(如 WebSocket 心跳),应慎用 fake timers,或仅 mock 关键路径
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











