在尝试简化为与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<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&lt; ..&gt;(..)并且通常更喜欢使用它,但在这种情况下它不是一个选项。
答案 0 :(得分:6)
问题源于Moq尝试解析提供的表达式树以创建参数匹配器的方式。你可以在这里找到来源: -
http://code.google.com/p/moq/source/browse/trunk/Source/MatcherFactory.cs
参考来源: -
It.IsAny<int>
匹配器(请参阅here)。考虑到这一点......
It.IsAny<int>
方法已在匹配器工厂之外进行了评估。因此,您将MemberAccess表达式设置为0。SendMatchFromMethod
被视为方法调用表达式,并且调用在MatcherFactory中进行评估。It.Is<Any>
第四次测试,说实话,应该通过,它似乎是一个疏忽,它被抛弃,可能只是因为它有点边缘情况。
最后Match.Create<T>
或MatchAttribute
可用于处理复杂谓词,也许它们可能适合您的用例?
答案 1 :(得分:3)
这似乎与我前一段时间的情况非常相似:Moq unit test with It.IsAny<DateTime>() fails
问题似乎与评估It.IsAny<int>()
时有关。通过2个测试,它将在Setup(...)
内进行评估,效果很好。在前2个失败的测试中,它的评估范围超出Setup(...)
的范围,因此无法正确评估。可能存储在您的变量中的是It.IsAny<int>()
的结果,这将是int
(0
)的默认值。
我不知道上次测试失败的确切原因,但它可能是作为优化,您的静态Func<int>
在Setup(...)
执行之前得到评估,或者可能是它在Setup(...)
之后得到评估,但无论哪种方式都发生在Setup(...)
之外。