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

TypeScript环境下使用Jest模拟模块内未导出内部函数的实现方法

如何模拟模块内未导出的函数进行单元测试?

最近写单元测试的时候卡壳了——我有个模块里的hello函数没导出,但被要测试的say调用了,现在想模拟hello却不知道怎么下手。先给大家看看我的代码:

file.ts

const hello = () => { console.log('hello'); }
export const say = () =>{
  hello()
  return 10;
}

file.test.ts

import { say } from 'file';
describe("Test ", () => {
  it("test", async () => {
    // 如何在此处模拟hello函数?
    say();
  })
})

下面给大家分享几种可行的解决方案:

方法1:用rewire库直接访问内部函数

rewire是个专门用来操作模块内部私有成员的工具,不管是CommonJS还是ES模块都能用。步骤很简单:

  1. 先安装依赖:
npm install --save-dev rewire
  1. 修改测试文件:
import rewire from 'rewire';
// 用rewire加载目标模块
const fileModule = rewire('./file');
import { say } from './file';

describe("Test ", () => {
  it("test", async () => {
    // 先保存原函数,测试完要恢复,避免影响其他用例
    const originalHello = fileModule.__get__('hello');
    // 把内部的hello替换成jest模拟函数
    fileModule.__set__('hello', jest.fn());

    say();

    // 验证模拟函数确实被调用了
    expect(fileModule.__get__('hello')).toHaveBeenCalled();

    // 恢复原函数
    fileModule.__set__('hello', originalHello);
  })
})

方法2:用Jest手动重写模块(仅适用于Jest环境)

如果你的项目用Jest做测试框架,可以直接通过jest.mock来手动构建模块,把内部的hello暴露出来并模拟:

import { say } from './file';

// 手动mock整个模块
jest.mock('./file', () => {
  // 先拿到原模块的所有内容
  const originalModule = jest.requireActual('./file');
  return {
    ...originalModule,
    __esModule: true, // 针对ES模块必须加这个标记
    hello: jest.fn(), // 把内部的hello暴露出来并替换成模拟函数
  };
});

// 从mock的模块里取出我们手动加的hello
const { hello } = jest.requireMock('./file');

describe("Test ", () => {
  it("test", async () => {
    say();
    // 验证模拟函数被调用
    expect(hello).toHaveBeenCalled();
  })
})

方法3:重构代码(推荐的长期方案)

其实如果经常需要测试内部私有函数,说明代码的耦合度有点高。更健康的做法是把hello拆到单独的模块,或者干脆导出它(如果只是测试用,可以加个注释说明,或者用// @ts-ignore忽略TypeScript的导出警告):

修改后的file.ts:

// 导出hello,仅用于测试
export const hello = () => { console.log('hello'); }
export const say = () =>{
  hello()
  return 10;
}

这样测试起来就简单多了,直接用jest.spyOn或者jest.mock就行:

import { say, hello } from './file';

describe("Test ", () => {
  it("test", async () => {
    // 用spyOn模拟hello函数
    const mockHello = jest.spyOn(require('./file'), 'hello').mockImplementation(() => {});
    
    say();
    
    expect(mockHello).toHaveBeenCalled();
    
    // 测试完恢复原函数
    mockHello.mockRestore();
  })
})

这种方案不仅解决了测试问题,还让代码的职责更清晰,后续维护也更方便。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 17:22:33