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

本文关键字:设置 Moq 验证 失败 函数调用 Is It 为什么 | 更新日期: 2023-09-27 18:12:41

在试图简化Setup/Verify匹配moq的复杂表达式树的创建时,我遇到了一些奇怪的行为。

假设我正在模拟下面定义的简单接口

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

下面的代码代表5个测试。对每个mockSender.Setup(...)进行一次测试。谁能解释一下为什么标为不合格的测试确实不合格?

[Test]
public void TestInlineSetup()
{
    const int expected = 5;
    var mockSender = new Mock<IService>(MockBehavior.Loose);
    //passes
    mockSender.Setup(s => s.Send(It.IsAny<int>())).Returns(expected);
    //fails
    var sendMatch = It.IsAny<int>();
    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<int>();
}
public static Func<int> SendMatchFromCurriedMethod()
{
    return () => It.IsAny<int>();
}

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

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

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

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

参考来源:-

  • It.IsAny<int>匹配器是通过编译和执行作为参数传递的表达式并查找任何匹配来检测的。
  • 上面的步骤只发生在方法调用和成员访问

记住这些....

  1. 第二次测试失败,因为It.IsAny<int>方法已经在匹配器工厂外进行了评估。因此,你有一个MemberAccess表达式0。
  2. 第三个测试通过,因为SendMatchFromMethod被视为一个方法调用表达式,并且调用在MatcherFactory中求值。
  3. 第四次测试失败的原因与第二次相同,函数已经评估,Moq无法检测到您实际上已经调用了It.Is<Any>
  4. 第五个测试失败,因为表达式被视为函数调用,而Moq没有对这种类型的表达式执行匹配器检查。

第四个测试,老实说,应该通过,这似乎是一个疏忽,它被留下了,可能只是因为它是一个边缘情况。

最后Match.Create<T>MatchAttribute可以用来处理复杂的谓词,也许它们适合您的用例?

这似乎与我前一段时间遇到的情况非常相似:使用它进行Moq单元测试。isany()失败

这个问题似乎是当It.IsAny<int>()得到评估。通过2个测试,它在Setup(...)内部被评估,它工作得很好。在失败的前2个测试中,它在Setup(...)的范围之外进行评估,因此无法正确评估。可能存储在变量中的是It.IsAny<int>()的结果,这将是int (0)的默认值。

我不知道最后一次测试失败的确切原因,但它可能是作为一个优化,你的静态Func<int>Setup(...)执行之前得到评估,或者它可能是在Setup(...)之后得到评估,但无论哪种方式,它都发生在Setup(...)之外。