模拟AMQPClient父类后调用子类方法Jest抛出is not a function错误
RabbitMQ服务Jest测试报错解决方案
问题场景
实现了继承自AMQPClient的RabbitMqService类,代码如下:
import { environment } from '@Utils/environment' import { AMQPChannel, AMQPClient } from '@cloudamqp/amqp-client' import { AMQPBaseClient } from '@cloudamqp/amqp-client/types/amqp-base-client' export class RabbitMqService extends AMQPClient { public connection?: AMQPBaseClient private lastDispatch: number = Date.now() public constructor() { super(environment.rabbitMqUrl) this.connection = undefined } public async connectToInstance(): Promise<AMQPBaseClient> { this.connection = await this.connect() return this.connection } }
编写Jest测试时尝试mock父类AMQPClient,测试代码如下:
import { RabbitMqService } from '../rabbit-mq.service' jest.mock('@cloudamqp/amqp-client', () => { return { ...jest.requireActual('@cloudamqp/amqp-client'), AMQPClient: jest.fn().mockImplementation(() => { return { connect: jest.fn().mockReturnThis() } }) } }); it('Should establish connection successfully', async () => { const rabbitMqService = new RabbitMqService(); await rabbitMqService.connectToInstance() })
运行测试时抛出is not a function错误,当前Jest配置:
import type { Config } from '@jest/types' const config: Config.InitialOptions = { verbose: true, transform: { '^.+\\.tsx?$': 'ts-jest' }, automock: true, moduleNameMapper: { '^Root/(.*)$': '<rootDir>/src', '^@Assets/(.*)$': '<rootDir>/src/assets/$1', '^@Data/(.*)$': '<rootDir>/src/data/$1', '^@Events/(.*)$': '<rootDir>/src/events/$1', '^@Guards/(.*)$': '<rootDir>/src/guards/$1', '^@Interceptors/(.*)$': '<rootDir>/src/interceptors/$1', '^@Services/(.*)$': '<rootDir>/src/services/$1', '^@Types/(.*)$': '<rootDir>/src/types/$1', '^@Utils/(.*)$': '<rootDir>/src/utils/$1' } } export default config
报错原因
- automock冲突:开启
automock: true后,Jest会自动mock所有依赖模块,和手动编写的jest.mock逻辑冲突,导致父类AMQPClient的mock实现异常。 - mock实现错误:当前mock的
AMQPClient返回的对象中,connect方法用mockReturnThis()返回子类实例,但connect实际应该返回AMQPBaseClient类型实例,类型不匹配导致调用时报错。 - 继承类mock不完整:直接mock
AMQPClient构造函数返回对象,没有正确模拟父类的原型链,导致子类继承的方法调用异常。
解决步骤
1. 修改Jest配置,关闭automock
在jest.config.ts中把automock: true改为false:
import type { Config } from '@jest/types' const config: Config.InitialOptions = { verbose: true, transform: { '^.+\\.tsx?$': 'ts-jest' }, automock: false, // 关闭自动mock moduleNameMapper: { '^Root/(.*)$': '<rootDir>/src', '^@Assets/(.*)$': '<rootDir>/src/assets/$1', '^@Data/(.*)$': '<rootDir>/src/data/$1', '^@Events/(.*)$': '<rootDir>/src/events/$1', '^@Guards/(.*)$': '<rootDir>/src/guards/$1', '^@Interceptors/(.*)$': '<rootDir>/src/interceptors/$1', '^@Services/(.*)$': '<rootDir>/src/services/$1', '^@Types/(.*)$': '<rootDir>/src/types/$1', '^@Utils/(.*)$': '<rootDir>/src/utils/$1' } } export default config
2. 修正AMQPClient的mock实现
确保mock的父类构造函数正确模拟原型链,并且connect方法返回符合AMQPBaseClient类型的模拟对象:
import { RabbitMqService } from '../rabbit-mq.service' import { AMQPBaseClient } from '@cloudamqp/amqp-client/types/amqp-base-client' // 模拟AMQPBaseClient实例 const mockAMQPBaseClient = {} as AMQPBaseClient jest.mock('@cloudamqp/amqp-client', () => { // 获取真实模块的类型定义,避免类型报错 const actual = jest.requireActual('@cloudamqp/amqp-client') return { ...actual, AMQPClient: jest.fn().mockImplementation(() => { return { // 让connect返回模拟的AMQPBaseClient实例 connect: jest.fn().mockResolvedValue(mockAMQPBaseClient) } }) } }); it('Should establish connection successfully', async () => { const rabbitMqService = new RabbitMqService(); const connection = await rabbitMqService.connectToInstance() // 验证connect方法被调用 expect(rabbitMqService.connect).toHaveBeenCalled() // 验证connection返回正确的模拟实例 expect(connection).toBe(mockAMQPBaseClient) })
3. 处理类型提示(可选)
如果遇到类型报错,可以用类型断言明确mock对象的类型,或者在测试文件顶部添加/* eslint-disable @typescript-eslint/no-explicit-any */临时忽略类型检查。
验证
运行Jest测试,此时应该可以正常执行,不再抛出is not a function错误,并且能验证connect方法的调用和返回值。
内容的提问来源于stack exchange,提问作者Karafra
相关产品推荐
相关产品推荐

