Jest中mockImplementation执行作用域及全局matchMedia配置问题
Jest执行作用域规则根因
Jest运行单测时有固定的执行优先级,你遇到的mock生效差异完全由这个规则导致:
- 首先扫描测试文件内所有
jest.mock调用,全部提升到所有import语句之前执行 - 按顺序执行所有import语句,完成所有依赖模块的导入
- 执行describe块内的顶层代码
- 执行测试用例前,先触发对应生命周期钩子(beforeEach/afterEach等),最后执行test内的业务代码
前两组测试报错的核心原因:
- 第一组报错大概率是模块导入阶段就触发了
fun()调用:如果SimpleInput组件或者util模块存在顶层执行的代码,在jest.mock执行完成、但mock实现还没完全注入的阶段就调用了fun(),就会拿到默认的undefined返回值。 - 第二组的
fun.mockImplementation写在describe顶层,执行时机在所有import完成之后,如果此时组件已经在导入阶段调用过fun(),使用了jest.mock('./util')默认生成的空mock函数(返回值为undefined),自然会报错。 - 第三组放在beforeEach里能生效,是因为beforeEach的执行时机刚好在组件渲染触发
fun()调用之前,完成了mock实现的注入。
全局window.matchMedia mock的正确配置方案
mockImplementation完全可以在全局setup文件里生效,你当前配置不生效是写法或者Jest基础配置错误导致的,按以下步骤调整即可:
- 首先确认Jest配置的基础项正确:
// jest.config.js 或 package.json的jest字段 module.exports = { // 其他配置省略 testEnvironment: 'jsdom', // Jest28+默认是node环境,必须指定jsdom才会生成window对象 setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'] // 路径要和你实际的setup文件路径匹配 }
- 调整setup文件内的mock写法,补充缺失的变量定义:
// src/setupTests.js 直接写入以下代码即可,不需要包裹任何钩子 const addListener = jest.fn(); Object.defineProperty(window, 'matchMedia', { writable: true, configurable: true, // 允许后续单测按需重写该属性 value: jest.fn().mockImplementation((query) => ({ matches: false, media: query, onchange: null, addListener, // 原代码未定义该变量直接使用,会抛出异常导致mock失效 removeListener: jest.fn(), addEventListener: jest.fn(), removeEventListener: jest.fn(), dispatchEvent: jest.fn(), })), });
- 调整完成后重启Jest即可,该全局mock会在所有测试文件执行前完成注入,不需要每个测试单独配置。
内容的提问来源于stack exchange,提问作者D Clover
相关产品推荐
相关产品推荐

