如何使用Jest mock返回带方法对象的函数及解决模块导入报错
Jest 模拟返回对象的工厂函数实现方案
原有业务代码结构
待模拟的 greeter 模块
const initGreeter = () => { return { sayHello: (name: string) => { console.log(`Hello: ${name}`) } } } export default initGreeter;
被测的 greeterConsumer 模块
import initGreeter from './greeter'; export const greetSomeone = (name: string) => { const greeter = initGreeter(); greeter.sayHello(name) }
测试覆盖目标
- 正常流程下,
greetSomeone会正确调用内部实例的sayHello方法并传入参数 initGreeter调用抛出异常时,错误会正确向外抛出
常见报错说明
实现过程中容易出现的报错:
TypeError: greeter_1.default is not a function
报错根源是 TypeScript 模块转译的默认导出逻辑和jest.mock返回值结构不匹配,两种可正常运行的mock配置方式:
- 显式声明ESModule结构的mock配置
直接在mock工厂函数中返回带__esModule: true标识的结构,将默认导出绑定到mock函数上:jest.mock('../../utils/greeter', () => ({ __esModule: true, default: jest.fn(() => mockGreeterInstance) }));
- 调整TypeScript编译配置
在tsconfig.json中开启"esModuleInterop": true后,可以简化mock写法,直接把默认导出设为mock函数:jest.mock('../../utils/greeter', () => jest.fn(() => mockGreeterInstance));
完整测试代码
import initGreeter from '../../utils/greeter'; import { greetSomeone } from '../../utils/greeterConsumer'; // 定义复用的mock greeter实例,用jest.fn包装内部方法,方便追踪调用记录 const mockGreeterInstance = { sayHello: jest.fn() }; // 配置jest.mock,选择上述任意一种合规写法即可 jest.mock('../../utils/greeter', () => ({ __esModule: true, default: jest.fn(() => mockGreeterInstance) })); describe('greeterConsumer', () => { afterEach(() => { jest.clearAllMocks(); }); it('正常流程下会调用sayHello并传入正确参数', () => { greetSomeone('sam'); // 验证sayHello被调用时传入了'sam'参数 expect(mockGreeterInstance.sayHello).toHaveBeenCalledWith('sam'); }); it('initGreeter抛出异常时,错误会正确向外抛出', () => { // 单次覆写initGreeter的实现,让其调用时抛出错误 (initGreeter as jest.Mock).mockImplementationOnce(() => { throw new Error('初始化greeter失败'); }); // 验证被测函数会同步抛出错误 expect(() => greetSomeone('sam')).toThrow(Error); }); });
内容的提问来源于stack exchange,提问作者IamaC
相关产品推荐
相关产品推荐

