首页 > 解决方案 > 如何使用 NSubstitute 在单元测试中模拟 MassTransit 标头值

问题描述

我正在尝试创建一个单元测试来测试我的 MassTransit (RabbitMq) 消费者。我的消费者需要一个名为UserName. 我已将我的消费者类简化如下。我需要找到一种方法来模拟总线头数据,否则测试在执行时总是会引发异常context.Headers.TryGetHeader("UserName", out object value)

我正在使用NSubstitute 模拟库。那么如何模拟ConsumeContext<MyDataObject>类型,并设置模拟头值呢?我更喜欢NSubstitute的解决方案

public class MyDataObjectCommand
{
    public string MyProperty { get; set; }
}

// Consumer class
public class MyConsumer : IConsumer<MyDataObjectCommand>
{
    private readonly IHubContext<MessageHub> _hubContext;

    public MyConsumer(IHubContext<MessageHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public async Task Consume(ConsumeContext<MyDataObjectCommand> context)
    {
        var myProperty = context.Message.MyProperty;
        var userName = TryGetHeader(context, "UserName");
        DoSomethingWith(_hubContext, myProperty, userName);
    }

    private string TryGetHeader(ConsumeContext<MyDataObjectCommand> context, string key, bool errorOnNull = true)
    {
        if (context.Headers.TryGetHeader(key, out object value))
        {
            return Convert.ToString(value);
        }

        if (errorOnNull)
        {
            throw new MyConsumerException(_reportName, $"{key} is not found", _hubContext);
        }

        return null;
    }
}


// Unit Test (I'm using XUnit)
public class MyConsumerTests
{
    private readonly IHubContext<MessageHub> _hubContext;

    public MyConsumerTests()
    {
       _hubContext = Substitute.For<IHubContext<MessageHub>>();
    }

    [Fact]
    public async Task ShouldConsume()
    {
        // arrange
        var mockDataObject = new MyDataObjectCommand() { MyProperty = "x" };

        var harness = new InMemoryTestHarness();
        harness.Consumer(() => new MyConsumer(_hubContext));

        // I need to mock header values on ConsumeContext<MyDataObjectCommand> object here
        // but how do I do it using NSubstitute?
        // context.Headers["UserName"] = "Bob";  ??

        await harness.Start();
        try
        {
            
            // act
            await harness.InputQueueSendEndpoint.Send(mockDataObject);

            // assert
            Assert.True(await harness.Consumed.Any<MyDataObjectCommand>());
            Assert.False(await harness.Consumed.Any<Fault<MyDataObjectCommand>>());
            //Assert.Equal(context.Header["UserName"], "Bob"); How do I do the assertion??
        }
        finally
        {
            await harness.Stop();
        }
    }
}

标签: c#unit-testingrabbitmqmasstransitnsubstitute

解决方案


使用 MassTransit 时不要模拟标头值,只需在使用测试工具发送消息时设置它们。对模拟框架的需求绝对为零。

await harness.InputQueueSendEndpoint.Send(mockDataObject, 
    context => context.Headers.Set(“UserName”, “Bob”));

ConsumeContext然后,当消息传递给消费者时,标头将可用。


推荐阅读