如何使用 Moq 测试 lambda 函数?
有一个函数:
public class MyCacheClass : ICache
{
public void T GetObject<T>(Func<T> func)
{
T t;
...
t = func();
...
return t;
}
}
public class MyWorkClass : IWork
{
public Object MyWorkMethod(string value)
{
return new object();
}
}
这些函数按以下方式调用:
public class MyTestableClass
{
public void MyTestableFunc(ICache cache, IWorkClass work)
{
string strVal="...";
...
Object obj = cache(()=>work.MyWorkMethod(strVal));
...
}
}
有必要为此编写一个 UnitTest(带有 Moq),并检查传递到“MaCacheClass.GetObject”的参数是否正确。
它应该是这样的:
[TestMethod]
public void MyTest()
{
Mock<ICache> mockCache = new Mock<ICache>();
Mock<IWorkClass> mockWorkClass = new Mock<IWorkClass>();
MyTestableClass testable = new MyTestableClass();
testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);
// should I check if 'MyCacheClass' was called with proper parameter?
mockCache.Verify(mock=>mock.GetObject(...)).Times.Once());
}
我如何提供适合“lambda 函数”的参数? 还有其他选择吗?
欢迎任何想法。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以更改该类,以便将 lambda 传递到该类中,而不是整个
IWorkClass
。然后您就可以存储并验证实际的 lambda。或者,您可以使用回调机制实际调用传入缓存的 lambda,然后验证是否在该模拟上调用了
IWorkClass.MyWorkMethod
。当缓存有价值时,这与缓存和工作类最终的使用方式相匹配,因此它还可以提供一个更好的有价值行为的示例,而不仅仅是单独测试方法。第三,您可以只使用
It.IsAny>()
并接受您必须通过检查来获得正确的位(只要更容易获得它)对的比错的多,这通常是可以的)。我通常会命名我的委托或 lambda 签名,因此我可能会遇到这个Func
问题。会成功的。作为第四种替代方案,推出一个小存根缓存,而不是使用模拟框架,然后从中获取并调用 lambda。尽管我非常喜欢
Moq
及其 Java 对应项Mockito
,但有时我发现,当我们承认这些工具不支持我们的功能时,单元测试更容易阅读和维护。正在尝试与他们做些什么。它只有几行代码,并且存根缓存很可能对其他单元测试也很有用。You could change the class so that you pass the lambda into the class, rather than the entire
IWorkClass
. Then you'd be able to store and verify against that actual lambda.Alternatively you could use the Callback mechanism to actually call the lambda which gets passed in to the cache, then verify that
IWorkClass.MyWorkMethod
got called on that mock instead. That matches how the cache and work class eventually get used, when the cache is valuable, so it could also provide a better example of the valuable behaviour than just testing the method in isolation.Third, you could just use
It.IsAny<Func<string, void>>()
and accept that you'll have to get that bit right by inspection (as long as it's easier to get it right than wrong this is usually OK). I normally name my delegate or lambda signatures so I may have got thisFunc<string, void>
thing wrong. It'll work out.As a fourth alternative, roll out a little stub cache instead of using a mocking framework, then grab and call the lambda from that. Much though I love
Moq
and its Java counterpartMockito
, sometimes I find unit tests are far easier to read and maintain when we just accept that the tools don't support what we're trying to do with them. It's only a few lines of code, and the chances are that the stub cache will be useful for other unit tests too.选项之一可能是:而不是在 Funct 参数上传递一些实现带有“GetObject”函数的接口的对象,例如:
在这种情况下,我想,我们还需要将 UnityContainer 注入到测试对象中,因此它将是类似的东西:
并为“MyCacheClass”的“GetObject”方法提供额外的测试,该测试将检查它是否调用 IGetObject 参数的“GetObject”方法...
PS 有点复杂的解决方案...所以,如果有人看到更好的解决方案,请建议!
One of the option could be: instead on Funct parameter pass some object that implement interface with 'GetObject' function, for example:
In this case, I guess, we need also to inject UnityContainer into tested object, therefore it will be something like that:
AND provide additional test for 'GetObject' method of 'MyCacheClass' that will check if it calls 'GetObject' method of the IGetObject parameter...
P.S. A little bit complicated solution... so, if anybody see the better one, please advise!