当从匿名函数调用It.Is …()时,为什么Moq设置/validation匹配器失败

当尝试简化为与Moq匹配的Setup / Verify匹配创建相当复杂的表达式树时,我遇到了一些奇怪的行为。

假设我在嘲笑下面定义的简单接口

public interface IService { int Send(int value); } 

以下代码表示5个测试。 每个mockSender.Setup(...)一个测试。 任何人都可以解释为什么标记为失败的测试失败了吗?

 [Test] public void TestInlineSetup() { const int expected = 5; var mockSender = new Mock(MockBehavior.Loose); //passes mockSender.Setup(s => s.Send(It.IsAny())).Returns(expected); //fails var sendMatch = It.IsAny(); mockSender.Setup(s => s.Send(sendMatch)).Returns(expected); //passes mockSender.Setup(s => s.Send(SendMatchFromMethod())).Returns(expected); //fails var sendMatch = SendMatchFromMethod(); mockSender.Setup(s => s.Send(sendMatch)).Returns(expected); //fails (this is somewhat contrived, but I have reasons for wanting to curry this) mockSender.Setup(s => s.Send(SendMatchFromCurriedMethod()())).Returns(expected); Assert.That(mockSender.Object.Send(expected), Is.EqualTo(expected)); } public static int SendMatchFromMethod() { return It.IsAny(); } public static Func SendMatchFromCurriedMethod() { return () => It.IsAny(); } 

编辑:我知道Mock.Of (..)并且通常更喜欢使用它但在这种情况下它不是一个选项。

问题源于Moq尝试解析提供的表达式树以创建参数匹配器的方式。 你可以在这里找到来源: –

http://code.google.com/p/moq/source/browse/trunk/Source/MatcherFactory.cs

参考来源: –

  • It.IsAny匹配器通过编译和执行作为参数传递并查找任何匹配项的表达式来检测(请参阅此处 )。
  • 上述步骤仅用于方法调用和成员访问

所以考虑到这一点….

  1. 第二次测试失败,因为已在匹配器工厂之外评估了It.IsAny方法。 因此,您将MemberAccess表达式设置为0。
  2. 第三次测试通过,因为SendMatchFromMethod被视为方法调用表达式,并且在MatcherFactory中计算调用。
  3. 第四次测试失败的原因与第二次相同,函数已经评估过,而且Moq无法检测到你实际上已经调用了它It.Is
  4. 第五次测试失败,因为Expression被视为函数调用,而Moq不对此类表达式执行匹配器检查。

说实话,第四个测试应该通过,它似乎是一个疏忽,它可能只是因为它有点边缘情况。

最后, Match.CreateMatchAttribute可用于处理复杂谓词,也许它们可能适合您的用例?

这似乎与我前一段时间遇到的情况非常相似: 使用It.IsAny ()的Moqunit testing失败

It.IsAny()被评估时,问题似乎就出现了。 通过2次测试后,它将在Setup(...)内部进行评估,该工作正常。 在前2个失败的测试中,它的评估超出了Setup(...)的范围,因此无法正确评估。 可能存储在变量中的是It.IsAny() ,它将是int0 )的默认值。

我不知道上次测试失败的确切原因,但它可能是作为优化,您的静态FuncSetup(...)执行之前得到评估,或者可能是它在之后得到评估Setup(...) ,但无论哪种方式都发生在Setup(...)