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

测试时如何覆写模块导入实现mock?能否不使用jest.mock?

不用jest.mock实现模块mock的可行性

你的实现思路在CommonJS(CJS)环境下完全可用,只需要修正代码里的小问题:你导入的foo是模块命名空间对象,不是可执行函数,测试用例里调用foo()必然报错,改成调用你mock的foo.bar()即可。
但如果你的项目已经切换到原生ES Modules(ESM)环境,这种写法会直接失败:ESM规范要求模块导出的绑定是只读的,模块命名空间对象的属性不可被重写,哪怕用Object.defineProperty强制配置可写也会被运行时拦截报错。

jest.mock的运行原理

jest.mock的核心逻辑是模块导入拦截,执行时机远早于业务代码的导入逻辑:

  • 首先jest在编译阶段会扫描代码,把所有jest.mock调用提升到当前模块的最顶部,优先级高于所有import/require语句
  • 针对CJS环境:直接修改Node的require缓存,后续所有导入目标模块的操作,拿到的都是jest返回的mock对象
  • 针对ESM环境:通过jest自定义的ESM加载器拦截静态导入请求,替换返回内容为你定义的mock模块

ESM环境下的兼容性

Node过渡到ESM后jest.mock依然可以正常使用,只需要做少量适配配置:

  • 在package.json中设置"type": "module"
  • jest配置文件中添加extensionsToTreatAsEsm: ['.ts', '.tsx', '.jsx'](根据你使用的后缀调整)
  • 若使用ts-jest,需额外在transform配置中开启useESM: true

如果确实不想用标准的jest.mock,ESM环境下可以用jest提供的jest.unstable_mockModule搭配动态导入实现自定义mock,示例如下:

// 先声明mock规则,必须放在动态导入之前
jest.unstable_mockModule('./foo', () => ({
  bar: jest.fn()
}))

describe('foo', () => {
  let mockFn: jest.Mock
  let bar: typeof import('./foo').bar

  beforeEach(async () => {
    // 动态导入模块,拿到mock后的导出
    const fooModule = await import('./foo')
    bar = fooModule.bar
    mockFn = bar as jest.Mock
  })

  it('Should not throw', () => {
    expect(() => bar()).not.toThrow()
  })
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 14:15:03