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

React中用Sinon/Jest假定时器测试setInterval调用失败问题

解决React假定时器测试setInterval和函数调用的问题

我来帮你搞定这个测试难题!你的问题主要出在假定时器的启用时机、setInterval的Mock方式以及组件挂载与Spy的顺序上,咱们一步步修正:

问题根源分析

  1. 假定时器生效太晚:你先渲染了组件再调用jest.useFakeTimers(),这时候componentDidMount已经用真实定时器执行了,假定时器完全没起作用。
  2. setInterval未被Mock:直接检查setInterval的调用次数会报错,因为它不是Jest的mock函数,需要先对它进行Spy或者Mock。
  3. Spy与组件挂载顺序错误:如果先渲染组件再SpycheckIdleTime,组件初始化时的函数调用不会被捕获到。

修正后的完整测试代码

import { shallow } from 'enzyme';
import PureMain from './PureMain';

// 假设authTimeoutSeconds是你定义的变量,示例设为5
const authTimeoutSeconds = 5;

describe('PureMain idle check', () => {
  let checkIdleTimeSpy;
  let setIntervalSpy;
  let wrapper;

  beforeEach(() => {
    // 1. 先启用假定时器,确保组件挂载时使用的是假定时器
    jest.useFakeTimers();
    
    // 2. Spy全局的setInterval,捕获它的调用行为
    setIntervalSpy = jest.spyOn(window, 'setInterval');
    
    // 3. 提前Spy组件的checkIdleTime方法,确保能捕获初始化时的调用
    checkIdleTimeSpy = jest.spyOn(PureMain.prototype, 'checkIdleTime');
    
    // 4. 最后再渲染组件
    wrapper = shallow(<PureMain />);
  });

  afterEach(() => {
    // 清理Spy和假定时器,避免影响其他测试用例
    jest.restoreAllMocks();
    jest.useRealTimers();
  });

  it('should check the idle time after [authTimeoutSeconds] seconds of inactivity', () => {
    // 验证setInterval是否按预期调用
    expect(setIntervalSpy).toHaveBeenCalledTimes(1);
    expect(setIntervalSpy).toHaveBeenCalledWith(
      expect.any(Function), 
      authTimeoutSeconds * 1000
    );

    // 快进所有待处理的定时器,触发checkIdleTime调用
    jest.runAllTimers();

    // 验证checkIdleTime是否被执行
    expect(checkIdleTimeSpy).toHaveBeenCalledTimes(1);

    // 验证定时器实例的超时时间是否正确
    const idleCheckTimeout = wrapper.instance().state.idleCheck;
    expect(idleCheckTimeout).not.toBeNull();
    expect(idleCheckTimeout._idleTimeout).toBe(authTimeoutSeconds * 1000);
  });
});

关键修正点说明

  • 假定时器先启用:jest.useFakeTimers()必须在组件渲染前调用,这样组件componentDidMount里的setInterval才会使用Jest的假定时器。
  • Mock全局setInterval:通过jest.spyOn(window, 'setInterval')监视全局定时器函数,就能正常验证它的调用次数和参数了。
  • Spy时机提前:在渲染组件前就SpyPureMain.prototype.checkIdleTime,确保组件初始化时的函数调用能被捕获。
  • 清理测试环境:在afterEach里恢复所有Mock和真实定时器,防止不同测试用例之间互相干扰。

如果你的Jest版本是27+,可以尝试用jest.runOnlyPendingTimers()替代jest.runAllTimers(),避免触发不必要的递归定时器(如果存在的话)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:39:08