如何用TypeScript的Jest Mock/Spy模拟未导出内部函数?
解决Jest Mock未导出内部函数的问题
问题原因
你之前的方法失效的核心原因是:generateSuf 和 generatePre 是模块内未导出的私有函数,create 直接调用的是模块内部的函数引用,而非导出对象上的方法。jest.mock 覆盖导出对象或者 jest.spyOn 监听导出模块,都无法影响到内部的函数调用关系。
可行解决方案
方案1:重构代码,抽离私有函数到独立模块
这是最推荐的方案,既让测试更友好,也提升了代码可维护性:
- 新建工具模块(比如
src/services/flow-utils.ts),迁移并导出两个生成函数:
// flow-utils.ts export async function generateSuf(id) { return id + 1; } export async function generatePre(id) { return id - 1; }
- 在原模块中导入并使用:
// flow.ts import { generateSuf, generatePre } from './flow-utils'; async function create(id: string, name: string) { const [suf, pre] = await Promise.all([generateSuf(id), generatePre(id)]); return suf + name + pre; } export { create };
- 测试时直接mock工具模块:
jest.mock('../../src/services/flow-utils', () => ({ generateSuf: jest.fn().mockResolvedValue('1'), generatePre: jest.fn().mockResolvedValue('1'), })); it('should create a user', async () => { const result = await create('3', '4'); expect(result).toBe('141'); });
方案2:使用 rewire 访问模块内部函数
如果不想重构代码,可以用 rewire 库修改模块内部私有变量:
- 安装依赖:
npm install --save-dev rewire
- 在测试文件中使用:
const rewire = require('rewire'); const flowModule = rewire('../../src/services/flow'); it('should create a user', async () => { // 替换内部的generateSuf和generatePre flowModule.__set__('generateSuf', jest.fn().mockResolvedValue('1')); flowModule.__set__('generatePre', jest.fn().mockResolvedValue('1')); // 从rewire模块中获取create方法 const { create } = flowModule; const result = await create('3', '4'); expect(result).toBe('141'); });
方案3:利用Jest模块模拟结合函数重写(不推荐,依赖实现细节)
此方法依赖Jest的模块处理逻辑,稳定性较差,仅适合临时场景:
import * as flowModule from '../../src/services/flow'; it('should create a user', async () => { // 用@ts-ignore规避TypeScript类型检查 // @ts-ignore flowModule.generateSuf = jest.fn().mockResolvedValue('1'); // @ts-ignore flowModule.generatePre = jest.fn().mockResolvedValue('1'); const result = await flowModule.create('3', '4'); expect(result).toBe('141'); });
注意:该方法在部分TypeScript编译配置下可能失效,因为未导出函数会被编译为模块内部私有变量,不会挂载到导出对象上。
内容的提问来源于stack exchange,提问作者Tomer Fikler
相关产品推荐
相关产品推荐

