编程语言
首页 > 编程语言> > c# – 单元测试使用异步调用的MassTransit消费者

c# – 单元测试使用异步调用的MassTransit消费者

作者:互联网

我们正在使用MassTransit异步消息传递(在RabbitMQ之上)来实现我们的微服务架构.

我们遇到了测试消费者的问题,而这些消费者又会进

下面的示例显示了一个简单的MassTransit使用者,它使用RestSharp进行出站调用并使用ExecuteAsync异步方法.

public class VerifyPhoneNumberConsumer : Consumes<VerifyPhoneNumber>.Context
{
    IRestClient _restClient;
    RestRequest _request;
    PhoneNumber _phoneNumber;
    PhoneNumberVerificationResponse _responseData;

    public VerifyPhoneNumberConsumer(IRestClient client)
    {
        _restClient = client;
    }

    public void Consume(IConsumeContext<VerifyPhoneNumber> context)
    {
        try
        {
            //we can do some standard message verification/validation here 

            _restClient.ExecuteAsync<PhoneNumberVerificationResponse>(_request, (response) =>
            {
                //here we might do some standard response verification

                _responseData = response.Data;

                _phoneNumber = new PhoneNumber()
                {
                    Number = _responseData.PhoneNumber
                };

                context.Respond(new VerifyPhoneNumberSucceeded(context.Message)
                {
                    PhoneNumber = _phoneNumber
                });
            });
        }
        catch (Exception exception)
        {
            context.Respond(new VerifyPhoneNumberFailed(context.Message)
            {
                PhoneNumber = context.Message.PhoneNumber,
                Message = exception.Message
            });
        }
    }
}

此示例单元测试可能如下所示:

[TestFixture]
public class VerifyPhoneNumberConsumerTests
{
    private VerifyPhoneNumberConsumer _consumer;
    private PhoneNumber _phoneNumber;
    private RestResponse _response;
    private VerifyPhoneNumber _command;

    private AutoResetEvent _continuationEvent;
    private const int CONTINUE_WAIT_TIME = 1000;

    [SetUp]
    public void Initialize()
    {
        _continuationEvent = new AutoResetEvent(false);
        _mockRestClient = new Mock<IRestClient>();
        _consumer = new VerifyPhoneNumberConsumer(_mockRestClient.Object);
        _response = new RestResponse();
        _response.Content = "Response Test Content";
        _phoneNumber = new PhoneNumber()
        {
            Number = "123456789"
        };
        _command = new VerifyPhoneNumber(_phoneNumber);
    }

    [Test]
    public  void VerifyPhoneNumber_Succeeded()
    {
        var test = TestFactory.ForConsumer<VerifyPhoneNumberConsumer>().New(x =>
        {
            x.ConstructUsing(() => _consumer);
            x.Send(_command, (scenario, context) => context.SendResponseTo(scenario.Bus));
        });

        _mockRestClient.Setup(
            c =>
            c.ExecuteAsync(Moq.It.IsAny<IRestRequest>(),
                                                            Moq.It
                                                               .IsAny<Action<IRestResponse<PhoneNumberVerificationResponse>, RestRequestAsyncHandle>>()))
                                                               .Callback<IRestRequest, Action<IRestResponse<PhoneNumberVerificationResponse>, RestRequestAsyncHandle>>((
                                                                   request, callback) =>
                                                               {
                                                                   var responseMock = new Mock<IRestResponse<PhoneNumberVerificationResponse>>();
                                                                   responseMock.Setup(r => r.Data).Returns(GetSuccessfulVericationResponse());
                                                                   callback(responseMock.Object, null);
                                                                   _continuationEvent.Set();
                                                               });


        test.Execute();

        _continuationEvent.WaitOne(CONTINUE_WAIT_TIME);

        Assert.IsTrue(test.Sent.Any<VerifyPhoneNumberSucceeded>());
    }

    private PhoneNumberVerificationResponse GetSuccessfulVericationResponse()
    {
        return new PhoneNumberVerificationResponse
            {
                PhoneNumber = _phoneNumber
            };
    }
}

由于在消费者中调用ExecuteAsync方法,如果我们在发出信号(或超时)之前没有放置阻止它的东西,那么这种测试方法就会失效.在上面的示例中,我们使用AutoResetEvent从回调发出信号以继续并运行断言.

这是一种可怕的方法,我们正在耗尽所有资源,试图找出替代方案.如果不明显,这可能会在测试期间导致错误的故障和竞争条件.没有提到潜在的自动化测试时间.

我们有哪些替代品比我们现有的更好.

编辑这是我最初用于模拟RestSharp异步调用的源代码.

How to test/mock RestSharp ExecuteAsync(…)

解决方法:

老实说,执行异步方法的复杂性是MassTransit 3的关键驱动因素之一.虽然还没有准备好,但它使消费者的异步方法调用变得更好.

您正在测试上面的内容,因为您在REST客户端上调用ExecuteAsync(),而不是等待消费者中的响应(使用.Result或.Wait),在消息使用者返回后,HTTP调用仍在继续.这可能是你问题的一部分.

在MT3中,此消费者将被写为:

public async Task Consume(ConsumeContext<VerifyPhoneNumber> context)
{
    try
    {
        var response = await _restClient
            .ExecuteAsync<PhoneNumberVerificationResponse>(_request);
        var phoneNumber = new PhoneNumber()
        {
            Number = response.PhoneNumber
        };

        await context.RespondAsync(new VerifyPhoneNumberSucceeded(context.Message)
        {
            PhoneNumber = _phoneNumber
        });
    }
    catch (Exception exception)
    {
        context.Respond(new VerifyPhoneNumberFailed(context.Message)
        {
            PhoneNumber = context.Message.PhoneNumber,
            Message = exception.Message
        });
    }        
}

标签:c,asynchronous,async-await,restsharp,masstransit
来源: https://codeday.me/bug/20190623/1275351.html