单元测试防抖与节流函数应聚焦行为验证:用Jest fake timers精确控制时间,检查调用次数、时机、参数及返回值;覆盖单次/多次触发、取消、首尾执行等场景,避免依赖真实定时器。

防抖(Debounce)和节流(Throttle)函数的单元测试,核心是验证其时间控制逻辑与调用行为是否符合预期——重点不是测 setTimeout,而是测“在特定输入序列和时间间隔下,回调是否被正确延迟、合并或限频执行”。
明确测试边界:只测行为,不测实现细节
避免断言内部用了几个 setTimeout 或是否清除了 timer ID。应关注:
- 函数是否被调用(次数、时机、参数)
- 返回值是否符合设计(如防抖返回取消函数)
- 多次触发时是否满足“延迟执行”或“固定频率执行”的契约
例如:对防抖函数 debounce(fn, 100),连续调用 5 次,应只在最后一次调用后约 100ms 执行一次 fn,且参数为最后一次调用传入的值。
用 Jest 的 fake timers 精确控制时间流
Jest 提供 jest.useFakeTimers() + jest.advanceTimersByTime(ms) / jest.runAllTimers(),可跳过真实等待,可靠验证时间逻辑:
立即学习“Java免费学习笔记(深入)”;
-
jest.useFakeTimers('modern')推荐(兼容 Promise、requestAnimationFrame) - 每次测试前
jest.clearAllTimers()防止干扰 - 用
jest.advanceTimersByTime(100)模拟流逝 100ms,比await waitFor(() => ..., { timeout: 110 })更稳定
示例(防抖测试片段):
it('calls callback once after delay on repeated calls', () => {
const fn = jest.fn();
const debounced = debounce(fn, 100);
debounced('a');
debounced('b');
debounced('c');
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(100); // 跳过等待
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenCalledWith('c'); // 取最后一次参数
});
覆盖关键场景:防抖与节流需分别验证的核心用例
防抖(Debounce)必测:
- 单次调用 → 延迟后执行一次
- 高频连续调用 → 仅最后一次触发后的延迟执行
- 手动取消(如返回的 cancel 函数)→ 后续 timer 不再触发
- 立即执行模式(leading edge)→ 首次调用立刻执行,后续仍防抖
节流(Throttle)必测:
- 首次调用 → 立即执行(trailing 默认不执行,leading 可配)
- 持续高频调用 → 每 wait ms 最多执行一次(如 100ms 间隔内只执行第 1 次和第 101ms 后第 1 次)
- 节流中再次触发 → 不重置周期,按固定窗口推进(注意与防抖的本质区别)
- 支持 trailing(结尾执行)时,最后一次应在周期结束时补调
避免常见陷阱:异步断言、清理与可重现性
不要写 setTimeout(..., 100); expect(...) —— 这依赖真实时间,易失败且慢。坚持用 fake timers。
每个测试后确保:
-
jest.useRealTimers()(或统一在afterEach中恢复) -
jest.clearAllMocks()清理 spy 状态 - 不复用同一防抖/节流实例跨测试(闭包状态会污染)
测试名直白具体,例如:'throttle with leading: calls immediately and then every 50ms',而非 'works correctly'。


















