0

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:

  1. 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?

  2. 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;
        }
    }
}
user3075478
  • 127
  • 11
  • 1
    See [this answer](https://stackoverflow.com/questions/62713786/masstransit-endpointconvention-azure-service-bus/62714778#62714778) on why endpoint conventions are bad. Then, recognize that you should not be using `IBus` in a consumer at all. Also, you shouldn't be creating the request client the way you are in the test harness either. – Chris Patterson Aug 01 '23 at 17:13
  • 1
    I'd suggest reading the [testing documentation](https://masstransit.io/documentation/concepts/testing). – Chris Patterson Aug 01 '23 at 17:14
  • Hi Chris. Thanks for your answer. I ended up removing that consumer and now I'm using a standard service class that in turn is using IBus. If I understood correcly I should be using the "IPublishEndpoint" interface but it's not so clear to me how to configure MassTransit in Program.cs. With method Send and the EndpointConvention works fine... – user3075478 Aug 01 '23 at 17:36

0 Answers0