MassTransit Consumer Unit Testing Using Asynchronous Calls

We use MassTransit asynchronous messaging (on top of RabbitMQ) for our microservice architecture.

We ran into problems testing consumers, which in turn make asynchronous calls.

The following example shows a simple MassTransit consumer that uses RestSharp for an outgoing call and uses the asynchronous ExecuteAsync method .

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
            });
        }
    }
}

An example unit test for this might look like this:

[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 .

, , . , . .

, , .

EDIT , , RestSharp.

/mock RestSharp ExecuteAsync (...)

+4
2

, MassTransit 3. , .

, ExecuteAsync() REST- ( .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
        });
    }        
}
+4

, . , .

RestSharp , :

VerifyPhoneNumberConsumer: Consumes.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 

        var response = await _restClient.ExecuteGetTaskAsync<PhoneNumberVerificationResponse>(_request);

        _responseData = response.Data;

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

}

async TPL RestSharp, .

- :

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

    var response = (IRestResponse<PhoneNumberVerificationResponse>)new RestResponse<PhoneNumberVerificationResponse>();
    response.Data = GetSuccessfulVericationResponse();

    var taskResponse = Task.FromResult(response);
    Expect.MethodCall(
        () => _client.ExecuteGetTaskAsync<PhoneNumberVerificationResponse>(Any<IRestRequest>.Value.AsInterface))
          .Returns(taskResponse);

    test.Execute();

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

All Articles