测试 settimeout 回调的关键是控制时间流逝而非真实等待,推荐用 jest fake timers(jest.usefaketimers()、jest.advancetimersbytime() 等),或手动 mock settimeout;异步场景需配合 await 和微任务清理,避免竞态和未清除定时器问题。

测试 setTimeout 回调逻辑的关键是**控制时间流逝,避免真实等待**。JavaScript 本身不提供内置的“快进时间”能力,所以得借助测试框架或手动模拟定时器。
使用 Jest 的 fake timers(推荐)
Jest 内置了对定时器的模拟支持,能完全接管 setTimeout、setInterval 等,让测试快速、可预测、无需等待。
- 在测试前调用
jest.useFakeTimers(),Jest 就会拦截所有定时器调用 - 用
jest.runAllTimers()立即执行所有已注册但未触发的定时器回调 - 或用
jest.advanceTimersByTime(ms)模拟经过指定毫秒数,只触发在此期间到期的回调 - 测试结束后建议调用
jest.useRealTimers()恢复真实行为(尤其在 describe 外层 afterEach 中)
示例:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
test('should call callback after 100ms', () => {
const callback = jest.fn();
setTimeout(callback, 100);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(100);
expect(callback).toHaveBeenCalledTimes(1);
});
手动 mock setTimeout(适用于非 Jest 环境)
如果不用 Jest,可在测试中直接覆盖全局 setTimeout,改用同步立即执行的版本:
- 保存原始
setTimeout:const originalSetTimeout = setTimeout; - 在测试前重写它:
global.setTimeout = (cb) => cb(); - 确保测试后还原:
global.setTimeout = originalSetTimeout; - 注意:这种方式会丢失延时参数,适合只关心“是否被调用”,不验证具体延迟场景
测试异步逻辑时注意 Promise 链和 await
若定时器回调里返回 Promise 或涉及 async/await,需确保测试函数是 async,并正确 await:
- 用
await waitFor(() => expect(...).toBe(...))(如配合 React Testing Library) - 或在 Jest 中结合
jest.runAllTimers()后,再await Promise.resolve()确保微任务队列清空 - 避免只靠
setTimeout模拟异步断言——应聚焦于状态变化结果,而非“等多久”
避免常见陷阱
真实环境容易忽略但测试中很关键:
- 不要在测试中写
setTimeout(() => { ... }, 100); expect(...)—— 这会变成竞态,断言总在回调前执行 - 清除定时器(
clearTimeout)也要测试,可 mockclearTimeout并检查是否被调用 - 多个嵌套或重复定时器时,优先用
jest.advanceTimersByTime分步推进,比runAllTimers更可控
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










