You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 16:36:04