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

Jest无法覆盖Debounce装饰器修饰的函数,求解决方法

如何用Jest覆盖带Debounce装饰器的私有方法代码

问题

我需要用Jest覆盖带有@Debounce(100)装饰器的私有方法checkDataToPositionInStep,但运行npm run coverage时,装饰器下方的代码完全没被统计到覆盖。直接调用该方法也达不到预期效果,请问怎么实现这些代码的覆盖?

待覆盖代码

@Debounce(100)
private checkDataToPositionInStep(): void {
    
    const proposalConsultData = this.proposalConsultResponseStore.get();

    if(proposalConsultData?.documentProposalData?.length > 1) {
        this.fullScreenLoaderService.hideLoader();
        this.router.navigate(['proposta-enviada']);
        return;
    }

    if(proposalConsultData?.warrantyData?.plateUf) {
        this.fullScreenLoaderService.hideLoader();
        this.router.navigate(['upload']);
    }

    if(proposalConsultData?.bankData?.branchCode) {
        this.fullScreenLoaderService.hideLoader();
        this.scrollService.next(STEP_ACCORDION.DADOS_GARANTIA.STEP);
        this.stepperService.next(STEP_ACCORDION.DADOS_GARANTIA.ID);
        return;
    }
    
    this.fullScreenLoaderService.hideLoader();
    this.scrollService.next(STEP_ACCORDION.DADOS_BANCARIOS.STEP);
    this.stepperService.next(STEP_ACCORDION.DADOS_BANCARIOS.ID);
    return;
}

Debounce装饰器代码

export function Debounce(timeout: number): Function {
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        const original = descriptor.value;
        descriptor.value = function debounce(...args) {
            setTimeout(() => {
                original.apply(this, args);
            }, timeout);
        }
        return descriptor;
    }
}

现有测试尝试

it('Should call checkDataToPositionInStep with only bankData', () => {
    const proposalConsultMock = <any> {
      bankData: {
        branchCode: '01901'
      }
    };
    (facade as any).checkDataToPositionInStep(proposalConsultMock );
});

解决方案

问题核心是防抖装饰器的异步定时器未被触发,以及测试未正确mock依赖数据,按以下步骤修复:

1. 启用Jest假定时器

防抖逻辑依赖setTimeout,Jest默认同步执行测试,不会等待定时器触发。需要在测试前后切换假/真实定时器:

beforeEach(() => {
  jest.useFakeTimers(); // 启用假定时器模拟时间流逝
});

afterEach(() => {
  jest.useRealTimers(); // 测试结束后恢复真实定时器,避免影响其他用例
  jest.clearAllMocks(); // 清空所有mock调用记录
});

2. 正确mock依赖数据

原方法从proposalConsultResponseStore.get()获取数据,而非参数,所以测试中传参无效。需要mock该方法返回预设数据:

jest.spyOn(facade['proposalConsultResponseStore'], 'get').mockReturnValue({
  bankData: { branchCode: '01901' }
});

同时mock其他服务的方法,方便后续断言执行结果:

const hideLoaderSpy = jest.spyOn(facade['fullScreenLoaderService'], 'hideLoader');
const scrollNextSpy = jest.spyOn(facade['scrollService'], 'next');
const stepperNextSpy = jest.spyOn(facade['stepperService'], 'next');

3. 触发定时器执行原方法

调用私有方法后,推进定时器到防抖时间(100ms),触发包裹在setTimeout里的原方法执行:

(facade as any).checkDataToPositionInStep();
jest.advanceTimersByTime(100); // 推进时间100ms,触发原方法逻辑

完整测试代码示例

beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
  jest.clearAllMocks();
});

it('Should handle checkDataToPositionInStep with only bankData', () => {
  // Mock store返回预设数据
  jest.spyOn(facade['proposalConsultResponseStore'], 'get').mockReturnValue({
    bankData: { branchCode: '01901' }
  });

  // Mock服务方法并监听调用
  const hideLoaderSpy = jest.spyOn(facade['fullScreenLoaderService'], 'hideLoader');
  const scrollNextSpy = jest.spyOn(facade['scrollService'], 'next');
  const stepperNextSpy = jest.spyOn(facade['stepperService'], 'next');

  // 调用私有方法
  (facade as any).checkDataToPositionInStep();

  // 推进定时器,触发原方法执行
  jest.advanceTimersByTime(100);

  // 断言方法调用是否符合预期
  expect(hideLoaderSpy).toHaveBeenCalled();
  expect(scrollNextSpy).toHaveBeenCalledWith(STEP_ACCORDION.DADOS_GARANTIA.STEP);
  expect(stepperNextSpy).toHaveBeenCalledWith(STEP_ACCORDION.DADOS_GARANTIA.ID);
});

覆盖其他分支

按照同样逻辑,修改proposalConsultResponseStore.get()的返回值,即可覆盖方法内的所有分支:

  • 返回documentProposalData.length > 1的数据,断言router.navigate(['proposta-enviada'])被调用
  • 返回warrantyData.plateUf存在的数据,断言router.navigate(['upload'])被调用
  • 返回无上述字段的数据,断言跳转到DADOS_BANCARIOS相关步骤

内容的提问来源于stack exchange,提问作者Felipe Ken

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 12:20:30