如何让Moq验证具有out参数的方法

时间:2015-05-29 14:57:36

标签: c# unit-testing nunit moq

我有一个接口定义,其中方法定义了out参数

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

我有以下使用上述界面的类

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

在我的单元测试中,我试图验证是否调用了RestCommunication对象的PerformPost方法。

但无论我做什么,我都无法让Moq验证方法是否被调用

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

但我收到以下错误

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.

如何验证方法是否被调用。

我正在使用Moq和NUnit

更新1

根据Sunny的评论,我修改了测试以使用如下的回调

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

这是执行测试时现在抛出的错误。

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)

Setup语句抛出此错误。

FYI。我正在使用Resharper 8并使用他们的测试运行器来执行我的测试。

如果我尝试将out参数添加到回调中,则代码将无法编译。

如果我将设置修改为

,我会收到同样的错误
_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" }
        });

2 个答案:

答案 0 :(得分:3)

最好实际使用AAA而不验证模拟。设置您的方法以返回一些特定结果并断言已返回结果:

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

以上将根据示例代码验证是否调用了该方法。

如果出于某种原因,问题中的代码并不真正代表真实代码,并且没有办法在返回方法时断言,而是可以使用Callback,并在错误中添加一些内容你可以验证。

类似的东西:

_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");

稍后验证apiErrors是否包含您在回调中插入的错误。

答案 1 :(得分:0)

在我正在处理的项目中,我们使用了 out It.Ref<T>.IsAny,其中 T 是输出参数类型(Moq 版本 4.14)。这是因为 out 用作参数修饰符,因此值为 passed by reference instead of by value

在您的情况下,verify 方法可能如下所示:

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

为确保没有其他意外调用发生,您还可以添加:

_mockRestCommunicationService.VerifyNoOtherCalls()