I'm trying to write some unit tests for testing a consumer.
Consumer code:
public sealed class QuotationMessageConsumer : IConsumer<SendQuotationMessage>
{
private readonly ILogger<QuotationMessageConsumer> _logger;
private readonly IBus _bus;
public QuotationMessageConsumer(ILogger<QuotationMessageConsumer> logger, IBus bus)
{
_logger = logger;
_bus = bus;
}
public async Task Consume(ConsumeContext<SendQuotationMessage> context)
{
_logger?.LogInformation("Sending message to topic...");
var request = context.Message;
if (request?.Quote is null || request?.State is null)
{
_logger?.LogError("Invalid request");
await context.RespondAsync(new BaseResult
{
ErrorMessage = "Invalid request",
Success = false
});
return;
}
try
{
var quotationMessage = BuildQuotationMessage(request.Quote, request.State);
await _bus.Send(quotationMessage);
_logger?.LogInformation("Message sent");
await context.RespondAsync(new BaseResult
{
Success = true
});
}
catch (Exception ex)
{
_logger?.LogError(ex, "An error occured while sending the message: {error}", ex.Message);
await context.RespondAsync(new BaseResult
{
Success = false,
ErrorMessage = ex.Message
});
}
}
}
I want to unit test all the paths.
I started writing the following unit test:
[Fact]
public async Task Consume_ValidRequest_SendsMessageToBus()
{
// Arrange
var provider = new ServiceCollection()
.AddMassTransitTestHarness(cfg =>
{
cfg.AddConsumer<QuotationMessageConsumer>();
})
.BuildServiceProvider(true);
var harness = provider.GetRequiredService<ITestHarness>();
await harness.Start();
try
{
var bus = provider.GetRequiredService<IBus>();
var client = bus.CreateRequestClient<SendQuotationMessage>();
// Act
await client.GetResponse<BaseResult>(BuildEmptySendQuotationMessage());
// Assert
Assert.True(await harness.Consumed.Any<SendQuotationMessage>());
var consumerHarness = provider.GetRequiredService<IConsumerTestHarness<QuotationMessageConsumer>>();
Assert.True(await consumerHarness.Consumed.Any<SendQuotationMessage>());
}
finally
{
await harness.Stop();
await provider.DisposeAsync();
}
}
And I have two issues:
Actually the consumer throws an exception when it tries to send a message using the IBus. It says: "'A convention for the message type QuotationMessage.QuotationMessage was not found" I think I have to mock the IBus. How am I supposed to mock it?
I'm not testing the BaseResult. How can I test it? UPDATE about 2). I can assert the result coming from "GetResponse".
Thanks for help :)
UPDATE
I ended up doing the following:
builder.Services.AddMassTransit(x =>
{
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.ClearSerialization();
cfg.UseJsonSerializer();
var busConfig = context.GetRequiredService<IOptions<BusConfig>>().Value;
cfg.Host(busConfig.ConnectionString);
cfg.Message<Message>(x =>
{
x.SetEntityName(busConfig.FaiMotorTopic);
});
});
});
Then I created the following service:
public class MessageService : IMessageService
{
private readonly ILogger<MessageService> _logger;
private readonly IPublishEndpoint _publishEndpoint;
/// <summary>
/// Constructor
/// </summary>
/// <param name="logger"></param>
/// <param name="publishEndpoint"></param>
public MessageService(ILogger<MessageService> logger, IPublishEndpoint publishEndpoint)
{
_logger = logger;
_publishEndpoint = publishEndpoint;
}
/// <inheritdoc/>
public async Task SendMessageAsync<T>(T message, CancellationToken cancellationToken = default) where T : class
{
_logger.LogInformation("Sending message...");
if (message == null)
throw new ArgumentNullException(nameof(message));
try
{
await _publishEndpoint.Publish(message, cancellationToken);
_logger.LogInformation("Message sent");
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occured while sending the message: {error}", ex.Message);
throw;
}
}
}