[英]What is the difference between passing It.IsAny<int>() and the value of It.IsAny<int>() to a method setup
我正在使用 Moq 并希望创建构建器类来创建具有预设合理默认值的模拟,这些默认值可以在测试设置期间根据需要覆盖。 我采用的方法使用扩展方法,在其中传递输入参数值和预期输出。 这样做时,我在我看来语义等效的代码中看到了不同的行为:在设置中直接传递 It.IsAny() 与在设置中间接传递 It.IsAny() 的值。 例子:
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
这两个调用是等效的(对我来说),但是对 Bar2 的调用失败了断言。 为什么是这样?
It.IsAny
只允许 Moq 匹配将来的方法调用调用(如果在Setup
构造中使用)。 当Setup
被调用时,Moq 只是将方法调用添加到已经设置的方法调用的缓存中。 请注意,示例中Setup
的参数类型为Expression<Func<IFoo, bool>>
。 由于您传入的是Expression
,因此不会调用实际的方法调用,并且 Moq 能够遍历表达式以确定方法调用的哪些参数是显式的,哪些是It.IsAny
参数。 它使用这种能力来确定运行时的未来方法调用是否与已经设置的方法调用之一匹配。
为了使方法Bar
可以接受参数It.IsAny<int>()
,有必要使It.IsAny<int>()
返回一个int
(因为这是Bar
的参数类型) . 通常, It.IsAny<T>
的返回类型必须是T
。 必须选择任意的T
值。 最自然的选择是default(T)
,它适用于引用类型和值类型。 ( 在此处阅读有关 default 关键字的更多信息)。 在您的情况下,这是default(int)
,即0
。
因此,当您实际评估It.IsAny<int>()
,会立即返回0
值。 但是,当您在Expression
使用It.IsAny<int>()
时(如在Setup
方法的参数中),则方法调用的树结构被保留,并且 Moq 可以将未来的方法调用与封装的方法调用进行匹配Expression
。
因此,尽管您不能以任何有意义的方式将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));
最后,我只想提醒您,Moq 是开源的。 来源可在此处获得。 我发现拥有该源代码很有价值,这样我就可以点击并浏览代码和单元测试。
It.IsAny<int>()
返回类型为 int 并返回0
,因此您的第二个设置等效于:
mock.Setup(x => x.Bar2(0)).Returns(true);
我没有检查起订量代码,但我很确定当它评估 setup 方法中的表达式时,它考虑到参数实际上是 It.IsAny 与正常数字。
如果您直接在辅助方法中创建设置,而不是通过 It.IsAny,您会更好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.