防抖测试需验证延迟执行、重置计时与取消功能,节流测试需验证首次立即执行、固定间隔限制及边界行为;均依赖 jest fake timers(如 advancetimersbytime)精确控制时间并检查回调次数与参数。

对防抖(debounce)和节流(throttle)函数写单元测试,核心是验证其时间控制逻辑是否符合预期:是否延迟执行、是否限制频次、是否能被正确清除。关键在于模拟时间、触发多次调用、检查回调执行次数与时机。
用 Jest 的 fake timers 控制时间流
Jest 内置的 jest.useFakeTimers() 是测试异步定时逻辑的首选。它让 setTimeout、clearTimeout 等同步可控,避免真实等待。
- 调用
jest.useFakeTimers()启用假计时器 - 用
jest.runAllTimers()立即执行所有待定定时器 - 用
jest.advanceTimersByTime(ms)推进指定毫秒(更精确,推荐用于防抖/节流) - 测试完记得
jest.useRealTimers()恢复,或在afterEach中重置
防抖函数测试要点与示例
防抖要求「最后一次触发后等待 delay 毫秒,才执行回调;期间新触发则重置计时器」。
- 触发多次,只在最后 delay 毫秒后执行一次回调
- 提前调用
clearTimeout(如返回的取消函数)应阻止执行 - 检查回调参数是否为最后一次调用时传入的值
示例(假设 debounce(fn, delay) 返回可取消函数):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
test('debounce executes only after delay since last call', () => {
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'); // 最后一次参数
});
test('debounce can be cancelled', () => {
const fn = jest.fn();
const debounced = debounce(fn, 100);
debounced('x');
const cancel = debounced.cancel; // 假设暴露 cancel 方法
cancel();
jest.advanceTimersByTime(100);
expect(fn).not.toHaveBeenCalled();
});
节流函数测试要点与示例
节流要求「固定间隔内最多执行一次,首次立即执行,后续在间隔结束后再执行」(常见“leading + trailing”模式需明确约定)。
- 连续快速调用,检查执行次数是否符合间隔限制(如 100ms 节流,1s 内最多执行 10 次)
- 验证首次调用是否立即执行(leading)
- 验证间隔末尾是否补发一次(trailing,若启用)
- 注意节流窗口的边界行为:第 1 次在 t=0 执行,第 2 次最早在 t=100 执行
示例(以 leading 节流为例):
test('throttle executes at most once per delay, first call immediate', () => {
const fn = jest.fn();
const throttled = throttle(fn, 100);
throttled('1'); // t=0 → 立即执行
throttled('2'); // t=0 → 被忽略
throttled('3'); // t=0 → 被忽略
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenCalledWith('1');
jest.advanceTimersByTime(50);
throttled('4'); // 仍在锁定期 → 忽略
expect(fn).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(50); // 到达 t=100 → 解锁,执行 pending 或下次调用
throttled('5'); // 触发执行
expect(fn).toHaveBeenCalledTimes(2);
expect(fn).toHaveBeenLastCalledWith('5');
});
补充建议:覆盖边界与清理
真实场景中容易忽略的点,测试里要主动覆盖:
-
零延迟或负延迟:传入
0或负数,应退化为立即执行或报错(按实现约定) - 高频连续调用 + 时间推进不完整:比如节流 100ms,只推进 99ms 后再调用,应仍被节流
-
多个实例独立性:不同
debounce(fn, 100)调用之间 timer 互不影响 - 内存与引用安全:确保没有意外闭包引用导致无法 GC(可通过弱引用或手动清空 mock 验证)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










