我有这个:
myObj.SetupSomething(It.IsAny<string>());
然后在MyObj类中:
public void SetupSomething(string s)
{
_someMock.Setup(c => c.DoWibble(s));
}
上面的代码不起作用,因为It.IsAny<>
通过null
它来设置它null
而不是任何字符串。如果我用这个替换它:
_someMock.Setup(c => c.DoWibble(It.IsAny<string>()));
然后就可以了。所以我想知道,是否可以将It.IsAny<string>()
through的表达式传递给我的方法,以便Moq仍能识别我在做什么,还是我需要做一个额外的SetupSomethingForAnyString()
方法(有点讨厌)?
如您所见,简单的答案是“否”,不可能仅将原始It.IsAny<string>()
结果作为参数传递给设置。但这还不是故事的结局。Moq如何检测表达式自变量并将其转换为匹配器,这在实际执行过程中提供返回值时最终会用到一些难以置信的细微差别。如果您有时间,我绝对建议您看一看源代码。那里发生了一些很酷的体操运动-不仅仅是表情树分析。
在最基本的层次上,在It.IsAny<string>()
设置之外用作参数不起作用的原因与执行顺序有关。考虑:
var x = It.IsAny<string>();
mock.Setup(m => m.DoWibble(x));
与
mock.Setup(m => m.DoWibble(It.IsAny<string>()));
首先,它It.IsAny<string>()
是在任何模拟上下文之外进行评估的,而在模拟上下文之外,该库实际上无法返回有意义的值,因此它只返回default(T)
。然后将该空值绑定为一个变量,该变量在带引号的lambda表达式中捕获m => m.DoWibble(x)
。
整个表达式都传递给该Setup
方法,Moq库将部分评估为参数提供的表达式,包括捕获的值“ x”。当前值为default(string)
,因此它将设置一个匹配器,该匹配器仅在传递空值时才起作用。
在第二个中,引用的表达式现在包括整个m => m.DoWibble(It.IsAny<string>())
调用。现在,当Moq部分执行时It.IsAny<string>()
,它可以在上下文中与观察者合作执行副作用,从而通知Moq库为该参数创建“ any”匹配器。
但这意味着MoqIt.IsAny
即使未作为原始引用的lambda的一部分直接调用,也可以检测到对方法的调用。这意味着它也可以正常工作:
Func<string> callIsAny = () => It.IsAny<string>();
mock.Setup(m => m.DoWibble(callIsAny.Invoke()));
这里的关键是It.IsAny<string>()
在设置之前实际上并未在语句中调用,而是仅在Moq分析模拟方法的参数时才进行评估。并且由于它设置了一个检测上下文来查看何时It.IsAny
调用,因此可以为这种情况正确地创建匹配器。
请注意,Moq使用指南中的任何地方均未对此进行记录,因此我不确定是否可以使用此功能。It.IsAny的检测上下文可能仅打算用于设置事件处理程序。但这当然很酷!
相反,如果您实际上想通过一些间接层传递参数匹配器,则最好利用Moq:中已经存在的功能It.Is
。
它将被定义为:
public void SetupSomething(Expression<Func<string, bool>> stringMatcher)
{
_someMock.Setup(m => m.DoWibble(It.Is(stringMatcher)));
}
像这样使用:
myObj.SetupSomething(_ => true); // match anything
myObj.SetupSomething(s => s == "a"); // match only calls with "a";
它不像使用普通It.IsAny
调用那样可读,但是您可以通过将自己的匹配器lambda作为属性或方法调用(例如Match.Any<string>()
)来缓解这种情况
到那时,看上去好像重新实现了Moq的领域语言。是否值得付出努力取决于您。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句