我正在使用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
Run Code Online (Sandbox Code Playgroud)
两个调用都是等价的(对我来说),但对Bar2的调用无法断言.为什么是这样?
Ben*_*ich 39
It.IsAny如果在Setup构造中使用,则仅允许Moq匹配方法调用的未来调用.当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(int)这样0.
所以当你真正评估时It.IsAny<int>(),0会立即返回值.但是,当您It.IsAny<int>()在Expression(在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));
Run Code Online (Sandbox Code Playgroud)
最后,我只想提醒您,Moq是开源的.来源可在此处获得.我发现拥有该源代码非常有价值,这样我就可以点击并探索代码和单元测试.