Moq设置:根据条件</t>传递值或It.IsAny <t>()的能力

时间:2013-11-21 12:36:54

标签: c# moq

[问题类似于:What is the difference between passing It.IsAny<int>() and the value of It.IsAny<int>() to a method setup - 需要进一步澄清]

短版

(以下更多定义)

我处于我需要在工厂设置模拟的情况。根据值,我想中选择使用它或It.IsAny<TTypeOfValue>()

这是我(天真)想要做的事情:

moq.Setup(() => mockWebService.WebServiceMethod(
    webServiceMethodObject.Some ?? It.IsAny<string>(), // that fails
    ...,
    out webServiceMethodObject.That,
    ...)).Returns(webServiceMethodObject.ReturnEnum);

我有如此大的参数列表(旧的遗留Web服务方法)和很多不同的值组合,我不想手工编写,也不想在任何地方使用It.IsAny<T>(),因为我想控制返回值取决于参数。

一个可能的替代方案应该是版本,我可以将具体值与具体返回类型匹配,当没有具体值可以匹配时,它会回退到最广泛的版本(所有参数都替换为It.IsAny<T>()。 (简短示例:在登录测试中,我想测试登录方法的不同输入参数的不同返回值。在所有其他测试中,我只想返回LoginSuccess)。

我怎样才能做到这一点?只有一些表达/反射魔法?


更长的解释

这就是我们典型的遗留Web服务的样子:

ReturnValueEnum WebServiceMethod(string some, int thing, ..., out int that, out byte[] those, ...) { ... }

我们需要这么多的webservice方法调用,而且它们的参数非常繁琐,我们必须将它们封装在对象中。 例如:

public class WebServiceMethodObject
{
    public string Some { get; set; }
    public int Thing { get; set; }
    ...
    public ReturnValue ReturnEnum { get; set; }
}

策略应该是这样的:我们正在创建此对象的默认版本。在测试中,我们填写了需要匹配的值。我们希望将对象传递给一个待编写的方法,该方法相应地设置模拟:

如果设置了属性:使用该值

其他:使用It.IsAny<T>()(请参阅上面的设置!)。

2 个答案:

答案 0 :(得分:6)

我会想到如果

webServiceMethodObject.Some ?? It.IsAny<string>()

是你想要但不起作用的,简单的替换就是

It.Is<string>(v =>
    webServiceMethodObject.Some == null ||
    webServiceMethodObject.Some == v)

答案 1 :(得分:2)

如果参数的逻辑非常复杂,您可以安全地对所有参数使用It.IsAny<T>并定义自定义委托来处理方法调用:

moq
    .Setup(() => WebServiceMethod(It.IsAny<string>(), It.IsAny<int>(), ...))
    .Returns((some, thing, ...) => {
        if (some == webServiceMethodObject.Some || webServiceMethodObject.Some == null) ...
        {
            return webServiceMethodObject.ReturnEnum;
        }
    });

如果您查看Returns重载列表,您会发现有很多选项,“硬编码”.Returns(some value)只是其中之一。

据我所知,传递给setup的全部内容是表达式树。然后,Moq检查参数的表达式子树。如果它们是基于It的表达式,则它们用于高级逻辑。如果不是,则评估它们并将值与输入匹配。这可能是您的天真版本不起作用的原因之一。