防抖函数测试需用 jest fake timers 模拟时间:先验证未提前执行,再推进不足延迟时间确认不触发,最后推进剩余时间验证正确调用;多次调用应重置计时器;需覆盖 cancel 清理逻辑及 promise 行为,避免混用 real timers 和微任务陷阱。

测试防抖函数(debounce)的异步延迟行为,核心是**控制时间、捕获调用时机、验证函数未提前执行且在延迟后正确触发**。不能依赖真实 setTimeout 等待,必须用 Jest 或其他测试框架的时间模拟能力。
使用 Jest 的 fake timers 模拟时间推进
Jest 提供 jest.useFakeTimers() 和 jest.advanceTimersByTime(ms),能精准控制防抖的等待窗口:
- 调用防抖函数后,立即检查目标函数是否未被调用(
expect(fn).not.toHaveBeenCalled()) - 用
jest.advanceTimersByTime(99)推进不足防抖间隔(如 delay=100),确认仍不触发 - 再推进剩余时间(如
jest.advanceTimersByTime(1)),验证此时函数被调用一次 - 若期间多次调用防抖函数,应只在最后一次调用后的 delay 毫秒后执行(即“重置计时器”)
验证防抖函数的返回值与清理逻辑
很多防抖实现会返回一个可取消的函数(如 cancel())。测试时需覆盖:
- 调用返回的 cancel 方法后,即使时间走完也不执行原函数
- cancel 后再次调用防抖函数,应开启新计时器(不报错、可正常工作)
- 若防抖函数返回 Promise(例如封装了异步操作),要测试该 Promise 在延迟后 resolve/reject,且 cancel 时 reject 或静默处理
避免常见陷阱
真实环境容易误判,测试中需特别注意:
- 勿混用
jest.useRealTimers()—— 会导致测试变慢且不稳定 - 每次 test 前用
jest.useFakeTimers(),结束后用jest.useRealTimers()或jest.clearAllTimers()隔离状态 - 防抖函数内部若用了
Promise.resolve().then()等微任务,fake timers 默认不处理;需加{ timers: ['setTimeout', 'setImmediate', 'requestAnimationFrame'] }或显式await Promise.resolve() - 不要用
setTimeout(() => { ... }, 100)断言,它绕过了 fake timers,失去可控性
简单示例(Jest)
假设你有:const debounce = (fn, delay) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; };
对应测试可写:
test('debounce calls fn after delay, and resets on repeated calls', () => {
jest.useFakeTimers();
const fn = jest.fn();
const debounced = debounce(fn, 100);
debounced('a');
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(50);
debounced('b'); // 重置计时器
jest.advanceTimersByTime(50); // 到 100ms,但上次是新调用,还没到它的 100ms
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(50); // 再走 50ms,共对第二次调用等待了 100ms
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenCalledWith('b');
});
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











