将It.IsAny ()和It.IsAny ()的值传递给方法设置有什么区别

我正在使用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(); mock.Setup(x => x.Bar(It.IsAny())).Returns(true); Assert.IsTrue(mock.Object.Bar(123)); // Succeeds var myValue = It.IsAny(); mock.Setup(x => x.Bar2(myValue)).Returns(true); Assert.IsTrue(mock.Object.Bar2(123)); // Fails 

两个调用都是等价的(对我而言),但对Bar2的调用无法断言。 为什么是这样?

It.IsAny仅允许Moq匹配方法调用的未来调用(如果在Setup构造中使用)。 调用Setup ,Moq只是将方法调用添加到已设置方法调用的缓存中。 请注意,示例中Setup的参数类型为Expression> 。 由于传入了一个Expression ,因此不会调用实际的方法调用,并且Moq能够遍历表达式以确定方法调用的哪些参数是显式的,哪些是It.IsAny参数。 它使用此function来确定运行时的未来方法调用是否与已设置的方法调用之一匹配。

为了使方法Bar可以接受参数It.IsAny() ,有必要使It.IsAny()返回一个int (因为这是Bar的参数类型) 。 通常, It.IsAny的返回类型必须为T 必须选择T的任意值。 最自然的选择是default(T) ,它适用于引用类型和值类型。 ( 在此处阅读有关default关键字的更多信息)。 在您的情况下,这是default(int) ,即0

因此,当您实际评估It.IsAny() ,会立即返回值0 。 但是,当您在Expression使用It.IsAny()时(如在Setup方法的参数中),则保留方法调用的树结构,并且Moq可以将将来的方法调用与封装的方法调用进行匹配。 Expression

因此,尽管您无法以任何有意义的方式将It.IsAny()为变量,但您可以将整个Expression保留在变量中:

 Expression> myExpr = x => x.Bar2(It.IsAny()); mock.Setup(myExpr).Returns(true); Assert.IsTrue(mock.Object.Bar2(123)); 

最后,我只想提醒您,Moq是开源的。 来源可在此处获得 。 我发现拥有该源代码非常有价值,这样我就可以点击并探索代码和unit testing。

It.IsAny()返回类型为int并返回0 ,因此您的第二个设置相当于:

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

我没有检查moq代码,但我很确定当它在setup方法中计算表达式时,它会考虑参数实际上是It.IsAny与正常数字。

如果直接在helper方法中创建设置,并且不传递It.IsAny,则最好。