使用Jest 29.7.0 Mock模块函数时遇只读属性赋值错误求助
解决Jest 29.7.0中ESM模块Async函数Mock失败的问题
你遇到的所有报错本质是ESM模块的导出是只读绑定,并非普通可修改的对象属性,所以针对CommonJS模块的jest.spyOn、直接赋值jest.fn()、Object.defineProperty等方法在ESM环境下不适用。以下是几种可行的解决方案:
方案1:使用jest.mock直接模拟ESM模块
这是ESM环境下最直接的mock方式,注意必须先执行jest.mock再导入被测试模块,否则ESM静态加载机制会优先加载原模块导致mock失效。
示例代码
message.js(原模块)
export async function send(content) { // 原业务逻辑 console.log('发送消息:', content); return 'success'; }
handler.js
import { send } from './message.js'; export async function handle(data) { const result = await send(data); return result; }
handler.test.js
// 先mock模块,再导入目标模块 jest.mock('./message.js', () => ({ send: jest.fn().mockResolvedValue('mock-success') })); import { handle } from './handler.js'; import { send } from './message.js'; test('handle方法调用mock的send函数', async () => { const result = await handle('test-data'); expect(send).toHaveBeenCalledWith('test-data'); expect(result).toBe('mock-success'); });
方案2:动态修改Mock返回值(结合jest.doMock)
如果需要在不同测试用例中动态调整mock行为,可以用jest.doMock配合模块缓存重置,实现灵活的mock配置。
test('动态修改send的mock返回值', async () => { jest.resetModules(); // 重置模块缓存,避免跨用例污染 const mockSend = jest.fn().mockResolvedValue('first-mock'); jest.doMock('./message.js', () => ({ send: mockSend })); // 动态导入模块 const { handle } = await import('./handler.js'); const { send } = await import('./message.js'); let result = await handle('test1'); expect(send).toHaveBeenCalledWith('test1'); expect(result).toBe('first-mock'); // 修改mock返回值 mockSend.mockResolvedValue('second-mock'); result = await handle('test2'); expect(result).toBe('second-mock'); });
方案3:通过Babel转为CommonJS(兼容传统Mock方式)
如果项目允许兼容CommonJS,可通过Babel将ESM转为CommonJS,这样就能使用jest.spyOn等传统mock方法。
步骤1:安装依赖
npm install --save-dev @babel/core @babel/preset-env babel-jest
步骤2:配置babel.config.json
{ "presets": [ ["@babel/preset-env", { "targets": { "node": "current" } }] ] }
步骤3:配置jest.config.js(如果存在)
module.exports = { transform: { '^.+\\.js$': 'babel-jest' } };
测试代码示例
import { handle } from './handler.js'; import * as message from './message.js'; test('用spyOn mock send函数', async () => { const spy = jest.spyOn(message, 'send').mockResolvedValue('spy-mock'); const result = await handle('test'); expect(spy).toHaveBeenCalledWith('test'); expect(result).toBe('spy-mock'); spy.mockRestore(); // 测试后恢复原函数 });
常见问题排查
- 确保运行命令正确:
NODE_OPTIONS="$NODE_OPTIONS --experimental-vm-modules" jest,这是ESM模式下Jest运行的必要配置。 - 禁止在导入模块后执行
jest.mock,ESM静态加载机制会导致mock无法覆盖原模块。 - 若出现
Cannot redefine property报错,说明你尝试修改ESM的只读导出绑定,必须改用模块级别的mock方式。
内容的提问来源于stack exchange,提问作者Ajouve
相关产品推荐
相关产品推荐

