我有一个使用 @golevelup/nestjs-rabbitmq 模块的有效 pub/sub 实现。
我想编写一个服务规范来测试 MessagingService 的发布方法,该方法调用 AmqpConnection 发布方法。如何在测试中模拟发布方法?
消息模块
import { RabbitModule } from '../rabbit/rabbit.module';
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MessagingService } from './messaging.service';
@Module({
imports: [
RabbitModule,
MessagingModule,
],
providers: [ConfigService, MessagingService],
controllers: [],
exports: [MessagingService],
})
export class MessagingModule {}
消息服务
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { QueueMessage } from 'src/models/queue-message.model';
import { Logger } from '@nestjs/common';
@Injectable()
export class MessagingService {
private readonly exchange: string = this.configService.get<string>('RABBITMQ_EXCHANGE') || 'drs-encrypt.exchange';
private readonly routingKey: string = this.configService.get<string>('RABBITMQ_ROUTING_KEY') || 'drs-encrypt';
private readonly logger = new Logger(MessagingService.name);
constructor(
private readonly configService: ConfigService,
private readonly amqpConnection: AmqpConnection
) {}
// Publish the queue message to the exchange
public async publish<T>(queueMessage: QueueMessage): Promise<void> {
this.logger.log(`publishing queueMessage to exchange: ${this.exchange} routing key: ${this.routingKey}`);
await this.amqpConnection.publish(this.exchange, this.routingKey, queueMessage);
}
}
消息服务 - 规范
import { Test, TestingModule } from '@nestjs/testing';
import { MessagingService } from '../messaging/messaging.service';
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'
import { ConfigService } from '@nestjs/config';
describe('MessagingService', () => {
let service: MessagingService;
type MockType<T> = {
[P in keyof T]?: jest.Mock<{}>;
};
const mockFactoryAmqpConnection: () => MockType<AmqpConnection> = jest.fn(() => ({
publish: jest.fn(() => AmqpConnection),
}));
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ConfigService,
MessagingService,
{
provide: AmqpConnection,
useFactory: mockFactoryAmqpConnection,
},
],
controllers: [],
}).compile();
service = module.get<MessagingService>(MessagingService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
您在模块设置中所做的似乎是正确的,但由于您没有提供测试用例,因此很难知道出了什么问题。
但是,您可以尝试提供一个简单的对象作为模拟,而不是工厂,并使用它来检查是否使用正确的输入调用它:
const amqpConnectionMock = {
publish: jest.fn()
};
const bindingMock = {
exchange: 'test_exchange',
routingKey: 'test_routing_key',
}
beforeEach(async () => {
jest.clearAllMocks(); // start fresh
const module: TestingModule = await Test.createTestingModule({
providers: [
MessagingService,
// you might want to mock the config too to provide a custom config for the test suite
{
provide: ConfigService,
useValue: {
get: () => bindingMock,
},
},
{
provide: AmqpConnection,
useValue: amqpConnectionMock,
},
],
controllers: [],
}).compile();
service = module.get<MessagingService>(MessagingService);
});
测试用例可能是这样的:
it('should publish', async () => {
const message: QueueMessage = { ... } // <- your message here
await service.publish(message);
expect(amqpConnectionMock.publish).toHaveBeenCalledWith(
bindingMock.exchange,
bindingMock.routingKey,
message,
);
});
P.S:与问题没有直接关系,但您也可以考虑在服务中注入记录器。这将允许在测试模块中替换它,以避免不必要的消息污染您的测试日志。