es module在jest中默认不支持动态mock,因静态导入绑定编译时;正确做法是用jest.unstable_mockmodule(jest≥29)配合await import()动态加载,或通过依赖注入规避模块mock复杂性。

ES Module 在 Jest 中默认不支持动态 mock(比如 jest.mock() 在 ESM 下不能在测试运行时按需替换模块),因为 ESM 的导入是静态的、编译时绑定的。要实现异步模块的动态 mock 行为,核心思路是:绕过静态 import,用 import() 动态导入 + Jest 的 jest.unstable_mockModule(Jest 29+)或借助 jest.requireActual + 手动替换导出对象。
使用 jest.unstable_mockModule(推荐,Jest ≥ 29)
这是 Jest 官方为 ESM 设计的动态 mock 方案,支持在测试中按需 mock 某个模块,并能处理异步导入场景:
- 必须在顶层(test 文件最外层)调用,不能放在
beforeEach或测试块内 - mock 后需用
await import()加载被测模块(不能用静态import) - 支持返回 Promise、模拟异步行为(如延迟、抛错)
示例:
// __tests__/fetch.test.jsjest.unstable_mockModule('../src/api.js', async () => ({
fetchData: jest.fn().mockResolvedValue({ data: 'mocked' }),
}));
<p>// ✅ 必须动态导入被测模块(它内部会 import '../src/api.js')
const { fetchAndProcess } = await import('../src/service.js');</p><p>test('fetchAndProcess returns processed data', async () => {
const result = await fetchAndProcess();
expect(result).toBe('PROCESSED: mocked');
});
</p>
配合 jest.isolateModules 避免模块缓存干扰
ESM 默认模块缓存(require.cache 不起作用),但多次 import() 可能复用同一模块实例。若需不同测试用不同 mock,需隔离模块上下文:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 在 Jest 配置中启用
isolateModules: true(jest.config.js) - 或在单个 test 文件顶部加
// @jest-environment node并确保每个测试都用独立import() - 避免跨测试复用 mock,每次测试都重新
unstable_mockModule+import()
手动 mock 导出对象(兼容 Jest
若无法升级 Jest,可用 jest.requireActual 获取原始模块,再覆盖其导出属性(适用于 CommonJS 兼容写法或 ESM + 构建工具如 Vite/Jest ESM 实验模式):
- 先
import * as api from '../src/api.js'(静态导入) - 用
jest.mock('../src/api.js')声明 mock - 在测试前用
api.fetchData.mockImplementation(...)设置行为 - ⚠️ 注意:此方式依赖 Jest 的 ESM 实验支持(
"type": "module"+ts-jest或babel-jest正确配置)
实际更稳妥的做法是:把被测逻辑封装成函数接收依赖(依赖注入),测试时直接传入 mock 函数,避开模块级 mock 复杂性。
处理真实异步副作用(如定时器、网络请求)
动态 mock 的目标常是控制异步行为节奏。除了 mockResolvedValue,还可:
- 用
mockImplementation(() => new Promise(...))自定义 resolve/reject 时机 - 结合
jest.useFakeTimers()控制setTimeout等,配合jest.advanceTimersByTime() - 对
fetch等全局 API,优先 mock 全局对象而非模块(global.fetch = jest.fn())
不复杂但容易忽略:ESM 动态 mock 的关键不是“怎么写 mock”,而是“何时加载被测模块”——必须用 await import() 触发新模块实例,才能让 mock 生效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










