应使用 import * as 导入模块后 spyon 其属性,如 jest.spyon(childclassmodule, 'getdata').mockresolvedvalue({ data: 'mocked' });禁用 jest.mock 工厂函数、对类成员 spy 或 beforeall 中 spy 等错误方式。

在 Jest 等测试框架中模拟 ES Modules 的导出函数(尤其是只读的命名导出,如 export async function GetData()),关键在于**不试图覆盖导出本身,而是对已导入的模块对象属性进行 spy**。ESM 的导出是只读绑定,直接赋值会报错;而 jest.spyOn() 作用于模块命名空间对象,完全合法且稳定。
✅ 正确做法:用 import * as 导入模块,再 spyOn 其属性
这是最可靠、符合 ESM 规范的方式。Jest 在 ESM 模式下能正确识别并代理模块的命名空间对象。
- 在测试文件中显式导入目标模块为命名空间对象:
import * as childClassModule from '../childClass'; - 在
beforeEach中调用:jest.spyOn(childClassModule, 'GetData').mockResolvedValue({ data: 'mocked' }); - 注意:若被模拟的是异步函数,必须用
mockResolvedValue(不是mockReturnValue) - 测试结束后建议调用
jest.restoreAllMocks()清理,避免测试间干扰
❌ 常见错误方式及原因
这些写法在 ESM 环境中容易失效或产生不可预期行为:
-
jest.mock('./childClass', () => ({ GetData: jest.fn().mockResolvedValue(...) })):工厂函数可能生成新模块实例,而被测代码加载的是原始模块(尤其未设resetModules: true时) -
jest.spyOn(MainClass, 'GetData'):GetData不是MainClass的成员,而是独立导出的函数,此举会报 “property not found” - 在
beforeAll中做spyOn:模块导入发生在describe外部,beforeAll执行可能早于模块实际解析完成,导致 spy 失效
? 补充:处理默认导出的模拟
如果目标函数是 export default async function getData(),需先确认导入方式:
- 若测试中用
import getData from '../getData',则不能直接spyOn默认导出(它不是对象属性) - 此时应改用命名空间导入:
import * as getDataModule from '../getData',再jest.spyOn(getDataModule, 'default').mockResolvedValue(...) - 或者,在源模块中改为命名导出(
export async function getData()),更利于测试和维护
⚙️ 环境准备小提醒
确保 Jest 配置支持 ESM:
- Jest 版本 ≥ 29,启用
type: "module"或使用.mjs后缀 - 在
jest.config.js中设置extensionsToTreatAsEsm: ['.js']并配合transformIgnorePatterns排除 node_modules - 测试文件本身也需是 ESM(含
import/export),不能混用require
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











