Jest UI单元测试时如何正确mock防抖(debounce)事件?
防抖封装后按钮点击单元测试失败问题解决方案
问题场景
为按钮点击逻辑封装了lodash防抖函数后,原有单元测试断言失败,移除防抖后测试可正常通过。
防抖逻辑代码如下:
import * as lodash from 'lodash'; //...中间逻辑省略 const buttonChangerTrue= lodash.debounce(() => buttonChanger(true, row), 500);
按钮点击后会触发API请求,最终修改图标颜色完成UI变更。
现有测试代码(精简后)
this.when.push({ beforeEachRender: () => { count++; jest.useFakeTimers(); if (count === 1) { this.mockHttpRequests(); } else { this.mockHttpRequestsReversed('buttonPressed'); } }, describe: 'When an unsaved search is ran and an agent is pinned', then: async () => { test('Then a note should be created', async () => { const button = tableBody.children[1].children[1].children[0].children[0]; const stickyNoteBeforePinning = this.getById(document.body, 'notes-69fc105ad2c94edf16efb1f4de125c38093aefe9') as HTMLElement; if (!button ) { throw 'button not found'; } await wait(() => { fireEvent.click(button); }); jest.runTimersToTime(1000); const stickyNoteAfterPinning = this.getById(document.body, 'notes-ec9c2a3041a18a4a7d8d8b4943292cb8aa92a2f5') as HTMLElement; expect(stickyNoteBeforePinning).toHaveAttribute('class', 'lead material-icons text-light'); expect(stickyNoteBeforePinning).not.toBe(stickyNoteAfterPinning); expect(stickyNoteAfterPinning).toHaveAttribute('class', 'lead material-icons text-pink'); // 此处断言失败 }); }, });
问题原因
旧版Jest默认的legacy假定时器仅能mock基础的setTimeout/setInterval,无法覆盖lodash防抖依赖的微任务、requestAnimationFrame等队列,导致防抖回调没有被触发,后续API请求和UI更新逻辑都没有执行,最终断言失败。
修复步骤
- 替换假定时器配置:将
beforeEachRender中的jest.useFakeTimers()修改为jest.useFakeTimers('modern'),确保所有lodash防抖用到的定时器类型都被正确mock。 - 调整定时器执行逻辑:点击按钮后,先调用
jest.advanceTimersByTime(500)走完防抖等待时间,再调用jest.runAllPendingTimers()确保防抖回调被执行。 - 等待异步UI更新:由于点击后有API请求的异步逻辑,跑完定时器后需要新增一轮
wait调用,等待UI渲染完成后再执行断言。 - 测试后清理:在测试用例结束后调用
jest.useRealTimers()重置定时器配置,避免影响其他测试用例。
修复后核心逻辑示例
await wait(() => { fireEvent.click(button); }); // 走完防抖等待时间 jest.advanceTimersByTime(500); // 执行所有待执行的定时器回调 jest.runAllPendingTimers(); // 等待API回调执行、UI更新完成 await wait(() => {}); // 再执行断言
内容的提问来源于stack exchange,提问作者BluePilot
相关产品推荐
相关产品推荐

