简体   繁体   English

传递 It.IsAny 和有什么区别<int> () 和 It.IsAny 的值<int> () 到方法设置

[英]What is the difference between passing It.IsAny<int>() and the value of It.IsAny<int>() to a method setup

I'm using Moq and want to create builder classes to create my mocks with preset reasonable defaults that can be overridden during test setup as needed.我正在使用 Moq 并希望创建构建器类来创建具有预设合理默认值的模拟,这些默认值可以在测试设置期间根据需要覆盖。 The approach I took uses extension methods in which I pass input parameter values and expected output.我采用的方法使用扩展方法,在其中传递输入参数值和预期输出。 In doing so, I'm seeing different behavior in what seems to me to be semantically equivalent code: passing It.IsAny() directly in a setup vs passing the value of It.IsAny() indirectly in a setup.这样做时,我在我看来语义等效的代码中看到了不同的行为:在设置中直接传递 It.IsAny() 与在设置中间接传递 It.IsAny() 的值。 Example:例子:

public interface IFoo
{
    bool Bar(int value);
    bool Bar2(int value);
}
public class Foo : IFoo
{
    public bool Bar(int value) { return false; }
    public bool Bar2(int value) { return false; }
}

var mock = new Mock<IFoo>();
mock.Setup(x => x.Bar(It.IsAny<int>())).Returns(true);
Assert.IsTrue(mock.Object.Bar(123));                    // Succeeds

var myValue = It.IsAny<int>();
mock.Setup(x => x.Bar2(myValue)).Returns(true);
Assert.IsTrue(mock.Object.Bar2(123));                   // Fails

Both calls are equivalent (to me), yet the call to Bar2 fails assertion.这两个调用是等效的(对我来说),但是对 Bar2 的调用失败了断言。 Why is this?为什么是这样?

It.IsAny only allows Moq to match future invocations of method calls if used within the Setup construct. It.IsAny只允许 Moq 匹配将来的方法调用调用(如果在Setup构造中使用)。 When Setup is called Moq just adds the method call to its cache of already-set-up method calls.Setup被调用时,Moq 只是将方法调用添加到已经设置的方法调用的缓存中。 Note that the argument to Setup in your example has type Expression<Func<IFoo, bool>> .请注意,示例中Setup的参数类型为Expression<Func<IFoo, bool>> Since you are passing in an Expression , the actual method call is not invoked and Moq has the ability to traverse the expression to figure out which parameters of the method call were explicit, and which are It.IsAny arguments.由于您传入的是Expression ,因此不会调用实际的方法调用,并且 Moq 能够遍历表达式以确定方法调用的哪些参数是显式的,哪些是It.IsAny参数。 It uses this ability to determine if a future method call at runtime matches one of the already-set-up method calls.它使用这种能力来确定运行时的未来方法调用是否与已经设置的方法调用之一匹配。

In order to make it so that the method Bar can accept argument It.IsAny<int>() , it is necessary to make It.IsAny<int>() return an int (since that is the type of the parameter of Bar ).为了使方法Bar可以接受参数It.IsAny<int>() ,有必要使It.IsAny<int>()返回一个int (因为这是Bar的参数类型) . In general, the return type of It.IsAny<T> must be T .通常, It.IsAny<T>的返回类型必须是T An arbitrary value of T must be chosen.必须选择任意的T值。 The most natural choice is default(T) , which works for reference types and value types.最自然的选择是default(T) ,它适用于引用类型和值类型。 (Read more about the default keyword here ). 在此处阅读有关 default 关键字的更多信息)。 In your case, that is default(int) , which is 0 .在您的情况下,这是default(int) ,即0

So when you actually evaluate It.IsAny<int>() the value of 0 is immediately returned.因此,当您实际评估It.IsAny<int>() ,会立即返回0值。 However, when you use It.IsAny<int>() in an Expression (as in the argument to the Setup method), then the tree structure of the method call is preserved and Moq can match future method invocations to the method call encapsulated by the Expression .但是,当您在Expression使用It.IsAny<int>()时(如在Setup方法的参数中),则方法调用的树结构被保留,并且 Moq 可以将未来的方法调用与封装的方法调用进行匹配Expression

So, although you cannot keep It.IsAny<int>() as a variable in any meaningful way, you can keep the entire Expression in a variable:因此,尽管您不能以任何有意义的方式将It.IsAny<int>()保留为变量,但您可以将整个Expression保留在变量中:

Expression<Func<IFoo, bool>> myExpr = x => x.Bar2(It.IsAny<int>());
mock.Setup(myExpr).Returns(true);
Assert.IsTrue(mock.Object.Bar2(123));  

Finally, I just want to remind you that Moq is open source.最后,我只想提醒您,Moq 是开源的。 The source is available here .来源可在此处获得 I find it valuable to have that source code so that I can click around and explore the code and the unit tests.我发现拥有该源代码很有价值,这样我就可以点击并浏览代码和单元测试。

It.IsAny<int>() has return type of int and returns 0 , so your second setup is equivalent to: It.IsAny<int>()返回类型为 int 并返回0 ,因此您的第二个设置等效于:

mock.Setup(x => x.Bar2(0)).Returns(true);

I didn't check the moq code, but I'm pretty sure that when it evaluates the expression in the setup method, it takes into account that the parameter is actually It.IsAny vs. a normal number.我没有检查起订量代码,但我很确定当它评估 setup 方法中的表达式时,它考虑到参数实际上是 It.IsAny 与正常数字。

You are better off if you create the setups directly in your helper methods, and not pass It.IsAny.如果您直接在辅助方法中创建设置,而不是通过 It.IsAny,您会更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM