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

Jest中setTimeout回调内ES6类方法Mock不生效的原因及解决方案

根因解析

该现象的核心诱因与setTimeout的事件循环特性直接相关,具体有两点:

  • 测试执行时序错位:空的测试用例没有任何等待逻辑,Jest在执行完同步代码、清空所有微任务队列后就会判定测试结束。但setTimeout属于宏任务,优先级在微任务之后——代码中setTimeout(..., 0)的回调,会在Jest已经退出测试流程后才被事件循环执行,此时测试环境已经完成上下文清理,自然无法拿到预期的Mock返回值。
    观察到的“同步链路中的.then()能拿到Mock”,本质是因为.then()的回调属于微任务,会在当前同步栈执行完成后立刻触发,仍处于Jest的测试等待窗口期内,因此可以正常获取Mock值。
  • Mock实现写法的缺陷:当前的jest.mock没有保留类的原型链路,搭配beforeEach中的mockClear()如果使用不当,会破坏之前定义的Mock返回逻辑,导致延迟回调中拿到的实例没有绑定预期的Mock方法。
修正方案

按照以下步骤调整,即可覆盖包含延迟回调的类方法Mock场景:

  • 接入Jest伪定时器接管所有setTimeout/setInterval的执行时序,无需等待真实时间流逝,即可确保定时器回调在测试断言前被执行
  • 保留原有ES6类的Mock结构,避免使用会破坏原型链的工厂函数写法
  • 测试断言阶段手动推进所有定时器执行、等待微任务队列清空,确保所有业务逻辑完成后再执行校验

修正后的测试代码参考:

import Util from '../src/test/util_test'
import Test from '../src/test/test'
import { mocked } from 'ts-jest/utils'

// 启用Jest伪定时器
jest.useFakeTimers();

jest.mock('../src/test/util_test', () => {
  return jest.fn().mockImplementation(() => ({
    getMemberAccounts: jest.fn().mockResolvedValue({
      accounts: [
        {
          id: '0zh8ap6luxoijne8qlfu1sg',
          name: 'test',
          officeNumber: '23',
          emailAddress: 'string@gmail'
        }
      ]
    })
  }));
});

describe('Test', () => {
  let testCase: Test;

  beforeEach(() => {
    // 仅清空Mock调用记录,不重置Mock实现
    mocked(Util).mockClear();
    // 清空上一个测试遗留的定时器队列,避免交叉污染
    jest.clearAllTimers();
    testCase = new Test();
  })

  test('test-case', async () => {
    // 快进执行所有待触发的定时器回调
    jest.runAllTimers();
    // 等待所有Promise微任务执行完成
    await Promise.resolve();

    // 此处可正常获取到Mock的返回值完成断言
    const utilInstance = mocked(Util).mock.instances[0];
    expect(utilInstance.getMemberAccounts).toHaveBeenCalledTimes(2);
  })

  afterAll(() => {
    // 测试执行完成后切回真实定时器,避免影响其他测试文件
    jest.useRealTimers();
  })
});
注意事项
  • 不要用mockReset()替换mockClear(),前者会完全清空定义的mockResolvedValue等实现逻辑,后者仅清除历史调用记录和实例栈,不会破坏Mock预设行为
  • 如果不想引入伪定时器,也可以在测试中手动等待一个宏任务周期,比如await new Promise(resolve => setTimeout(resolve, 0)),但这种方式执行效率低于伪定时器,容易受运行环境的随机延迟影响
  • 挂载在实例上的类方法(比如示例中的getMemberAccounts为箭头函数属性),不需要额外挂载到类原型上,只要Mock工厂返回的实例结构与原类保持一致即可正常Mock

内容的提问来源于stack exchange,提问作者Nguyễn Hoàng Hưng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 10:36:07