【问题标题】:How to get Moq to verify method that has an out parameter如何让 Moq 验证具有 out 参数的方法
【发布时间】:2015-05-29 14:57:35
【问题描述】:

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

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 语句中抛出此错误。

仅供参考。我正在使用 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" }
        });

【问题讨论】:

    标签: c# unit-testing nunit moq


    【解决方案1】:

    最好实际使用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 是否有您在回调中插入的错误。

    【讨论】:

    • 我尝试使用回调,现在我收到以下错误消息:无效回调。带参数的方法上的设置不能调用带参数的回调。我也尝试过使用通用方法调用。回调(s, m,e)
    • 您能否发布确切的异常消息。 if 应该包含什么是预期的参数,以及你设置了什么。
    【解决方案2】:

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

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

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

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

    _mockRestCommunicationService.VerifyNoOtherCalls()
    

    【讨论】:

      猜你喜欢
      • 2011-07-11
      • 1970-01-01
      • 2014-06-03
      • 2010-12-17
      • 2011-03-08
      • 2011-03-11
      • 2020-07-03
      • 2010-10-18
      • 2010-10-06
      相关资源
      最近更新 更多