如何用Jest框架Mock Node.js同模块中被调用的导出函数
问题分析与解决方案
第一种方法失效的原因
jest.mock是Jest的静态语法,它会被Jest在模块导入之前优先执行。你把jest.mock放在test函数内部时,模块../app1.js已经被导入并加载完毕,mock逻辑根本没机会生效。另外你还写错了ES模块标识:应该是__esModule(单数),不是__esModules,这也会导致mock无法被正确识别。
第二种方法报错的原因
ES模块的导出是只读属性,符合ES规范的限制,jest.spyOn本质是尝试修改这个只读属性,所以会抛出Cannot assign to read only property的错误。
正确解决方案
方案一:模块级静态Mock(推荐)
把jest.mock放在模块顶部,保留原模块的doubleSum实现,只mockadd函数:
import { jest } from "@jest/globals"; import * as app from "../app1.js"; // Jest会在导入app1.js之前执行这个mock jest.mock("../app1.js", () => { // 导入原模块,保留未被mock的函数实现 const originalModule = jest.requireActual("../app1.js"); return { __esModule: true, // 正确的ES模块标识 ...originalModule, // 展开原模块的所有导出 add: jest.fn(() => 60), // 仅mock add函数 }; }); describe("Mock", () => { test("doubleSum mock add", () => { expect(app.add(2, 3)).toBe(60); // doubleSum会使用mock后的add,计算结果为60*2=120 expect(app.doubleSum(2, 3)).toBe(120); }); });
方案二:单测试用例动态Mock
如果需要在单个测试中动态切换mock逻辑,可以使用jest.doMock配合动态导入:
import { jest } from "@jest/globals"; describe("Mock", () => { test("doubleSum mock add", async () => { jest.doMock("../app1.js", () => { const originalModule = jest.requireActual("../app1.js"); return { __esModule: true, ...originalModule, add: jest.fn(() => 60), }; }); // 动态导入模块,确保mock生效 const app = await import("../app1.js"); expect(app.add(2, 3)).toBe(60); expect(app.doubleSum(2, 3)).toBe(120); }); });
关键注意点
jest.mock必须放在模块级作用域(import之后、describe之前),才能在模块导入前生效。- ES模块导出只读,不能用
jest.spyOn直接修改导出的函数,必须通过模块mock实现。 - 务必写对
__esModule(单数),这是Jest识别ES模块mock的必要标识。
内容的提问来源于stack exchange,提问作者Dhana
相关产品推荐
相关产品推荐

