Darren Guy
Darren Guy

Reputation: 1153

How to get Moq to verify method that has an out parameter

I have an interface definition where the method has an out parameter defined

public interface IRestCommunicationService
{
    TResult PerformPost<TResult, TData>(string url, TData dataToSend, out StandardErrorResult errors);
}

I have the following class that is using the above interface

    public TripCreateDispatchService(IRestCommunicationAuthService restCommunicationService, ISettings settings)
    {
        _restCommunicationService = restCommunicationService;
        _settings = settings;
    }

    public FlightAlert CreateTrip(string consumerNumber, PostAlertModel tripModel, out StandardErrorResult apiErrors)
    {
        url = .. code ommited
        var result = _restCommunicationService.PerformPost<FlightAlert, PostAlertModel>(url), tripModel, out apiErrors);
        return result;
    }

In my unit tests I am trying to verify that the PerformPost Method of the RestCommunication object is called.

But no matter what I do, I cannot get Moq to verify that the method was called

    public void DispatchService_PerformPost()
    {
        var consumerNumber = ...
        var trip = ...
        var result = ...
        var apiErrors = new StandardErrorResult();
        ... code to setup mock data
        _mockRestCommunicationService = new  Mock<IRestCommunicationAuthService>();
        _mockEestCommunicationService.Setup(x => x.PerformPost<string, PostAlertModel>(It.IsAny<string>(), It.IsAny<PostAlertModel>(), out apiErrors)).Verifiable();


        _systemUnderTest.CreateTrip(consumerNumber, trip, out apiErrors);

        _mockRestCommunicationService.Verify(m => 
            m.PerformPost<StandardErrorResult, PostAlertModel>(
            It.IsAny<string>(), 
            It.IsAny<PostAlertModel>(), out apiErrors
            ), Times.Once);
    }

But I am receiving the following error

Moq.MockException : 

Expected invocation on the mock once, but was 0 times: 
m => m.PerformPost<StandardErrorResult,PostAlertModel>(It.IsAny<String>(), It.IsAny<PostAlertModel>(), .apiErrors)

No setups configured.

How do I go about verifying that the method was called.

I am using Moq and NUnit

UPDATE 1

As per the comment from Sunny, I have modified the test to use a callback as follows

var consumerNumber = ...
var trip = ...
var result = ...
StandardErrorResult apiErrors;
_mockRestCommunicationService.Setup(x => x.PerformPost<string, PostAlertModel>(
            It.IsAny<string>(), It.IsAny<PostAlertModel>(), out apiErrors))
    .Callback<string, PostAlertModel, StandardErrorResult>
    ((s, m, e) => e.Errors = new System.Collections.Generic.List<StandardError>()
    {
        new StandardError { ErrorCode = "Code", ErrorMessage = "Message" }
    });

_systemUnderTest.CreateTrip(consumerNumber, trip, out apiErrors);
Assert.That(apiErrors.Errors, Is.Not.Null);

This is the error that is now being thrown when executing the test.

System.ArgumentException : Invalid callback. Setup on method with 
parameters (String,PostAlertModel,StandardErrorResult&) 
cannot invoke callback with parameters (String,PostAlertModel,StandardErrorResult).
   at Moq.MethodCall.ThrowParameterMismatch(ParameterInfo[] expected, ParameterInfo[] actual)
   at Moq.MethodCall.SetCallbackWithArguments(Delegate callback)
   at Moq.MethodCallReturn`2.Callback(Action`3 callback)

This error is thrown at the Setup statement.

FYI. I am using Resharper 8 and using their test runner to execute my tests.

If I try to add the out parameter to the callback, the code will not compile.

I get the same error if I modify the setup to

_mockRestCommunicationService.Setup(x => x.PerformPost<string, PostAlertModel>(
It.IsAny<string>(), It.IsAny<PostAlertModel>(), out apiErrors))
.Callback
    ((string s, PostAlertModel m, StandardErrorResult e) => e.Errors = new System.Collections.Generic.List<StandardError>()
        {
            new StandardError { ErrorCode = "Code", ErrorMessage = "Message" }
        });

Upvotes: 15

Views: 8242

Answers (2)

pekaaw
pekaaw

Reputation: 2597

In the project I am working on we have used out It.Ref<T>.IsAny where T is the out parameter type (Moq version 4.14). This is because out is used as a parameter modifier, such that the value are passed by reference instead of by value.

In your case the verify method could look like this:

_mockRestCommunicationService.Verify(
        _ => _.PerformPost<StandardErrorResult, PostAlertModel>(
            It.IsAny<string>(), 
            It.IsAny<PostAlertModel>(),
            out It.Ref<StandardErrorResult>.IsAny
        ),
        Times.Once
    );

To make sure no other unexpected invocations happened, you can also add:

_mockRestCommunicationService.VerifyNoOtherCalls()

Upvotes: 11

Sunny Milenov
Sunny Milenov

Reputation: 22310

It's better to actually use AAA and not verify the mock. Setup your method to return some specific result and assert that the result have been returned:

var myCommResult = new PostAlertModel();
_mockEestCommunicationService
    .Setup(x => x.PerformPost<string, PostAlertModel>(It.IsAny<string>(), It.IsAny<PostAlertModel>(), out apiErrors)
    .Returns(myCommResult);

var response = _systemUnderTest.CreateTrip(consumerNumber, trip, out apiErrors);

Assert.AreSame(myCommResult, response);

The above will validate that the method was called, based on the example code.

If for some reason, the code in the question is not really representative of the real code, and there is no way to assert on the return of the method, than you can use Callback instead, and put something in the errors so you can verify.

Something like:

_mockEestCommunicationService
   .Setup(x => x.PerformPost<string, PostAlertModel>(It.IsAny<string>(), It.IsAny<PostAlertModel>(), out apiErrors))
   .Callback( (string s, PostAlertModel m, StandardErrorResult e) => e.Add("Some error to test");

And later verify that apiErrors has the error you inserted in the callback.

Upvotes: 4

Related Questions