You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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);
  });
});

关键注意点

  1. jest.mock必须放在模块级作用域(import之后、describe之前),才能在模块导入前生效。
  2. ES模块导出只读,不能用jest.spyOn直接修改导出的函数,必须通过模块mock实现。
  3. 务必写对__esModule(单数),这是Jest识别ES模块mock的必要标识。

内容的提问来源于stack exchange,提问作者Dhana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 11:29:54