Rhino Mocks 存根和模拟仅适用于接口?
Posted
技术标签:
【中文标题】Rhino Mocks 存根和模拟仅适用于接口?【英文标题】:Rhino Mocks stubs and mocks are only good for interfaces? 【发布时间】:2011-01-24 04:29:36 【问题描述】:Rhino Mocks 存根和模拟仅适用于接口,而不适用于具体类,这对吗?我花了很长时间试图让这段代码正常工作。我没想到存根的 pubSubClient 总是从类中调用 Send 方法。该方法有一些依赖并抛出异常。
[Test]
public void Test01()
PubSubMessage psm = new PubSubMessage();
var pubSubClient = MockRepository.GenerateStub<PubSubClient>();
pubSubClient.Stub(x => x.Send(psm)).IgnoreArguments().Return(null);
// actual PubSubClient Send method throws exception
// the rest of the test is skipped...
但是,当我提取接口并使用 IPubSubClient 运行相同的测试时,它似乎按预期工作。
这是否意味着我必须为我想用 Rhino 模拟/存根的每个类提取接口?还是我在技术上或概念上遗漏了什么?
更新:好的,看来我知道我遗漏了哪一部分: Rhino Mocks 无法拦截对非虚拟方法的调用。所以,我想我要么使用接口,要么使具体类上的每个方法都是虚拟的。如果还有其他选择,请纠正我。
【问题讨论】:
【参考方案1】:Bryan 使用部分模拟的答案不正确。这不是部分模拟的用途。
Jon Erickson 的回答基本正确:Rhino Mocks 和 Moq 无法拦截非虚拟调用,也无法拦截静态方法或属性。这意味着您不能伪造以下内容:
DateTime.Now; // static property, can't fake static property
someClass.SomeNonVirtualMethod(); // can't fake non-virtual method
sealedClass.Foo(); // can't fake anything on sealed classes
Utilities.SomeStaticMethod(); // can't fake static methods
someList.Any(); // can't fake extension methods like Linq's .Any()
TypeMock 可以伪造这些,正如 Jon 提到的那样。
需要注意的是,还有一个额外的模拟框架可以拦截所有调用:Microsoft Moles framework。它的工作方式与 TypeMock 相同,它使用 .NET 分析器 API 来拦截调用。
Moles 是免费的(目前)。它也是测试版。 Moles 仅适用于Microsoft Pex tools。而且它的 API 明显不如 TypeMock 精致、优雅的 API。
【讨论】:
如果您声明部分模拟不是为了某事,那么在您的回复中说明它们是为了什么会更有帮助。部分模拟旨在仅模拟类的一部分,这使得它们可以方便(并且需要)模拟一个普通模拟无法处理的抽象类。这允许测试抽象方法。部分模拟(至少在 Rhino 中)将模拟任何类,并且不限于抽象类,但是请注意是否存在将在返回时调用的实现代码。 (necro alert):我刚刚发现你的类甚至不需要抽象就可以使方法虚拟!这么小的事情,但我以前从未考虑过:-)【参考方案2】:您必须使方法虚拟化。 Rhino 模拟(和大多数其他隔离框架)利用代理类来创建存根/模拟。
如果你使用 TypeMock Isolator,你可以模拟任何东西,因为这个隔离框架利用 .NET Profiler API 来创建它的“存根/模拟”
【讨论】:
+1。正如他的更新中提到的 OP,这些方法必须是虚拟的。 Moq 也是如此,并且(我相信)除 TypeMock Isolator 之外的每个 .NET 隔离框架都是如此。廉价而简单的解决方法是使方法虚拟化(或提取接口)。【参考方案3】:这基本上是正确的,并且通常是良好的做法。但是,它仅对特定类型的编码真正有用。
不要将对象视为某些“更高权力”可以操纵的东西。相反,将它们视为可以相互发送消息的自主“人”。接口表示单个对象发送的消息。
然后您使用模拟来验证是否发送了正确的消息,而不是提供依赖项的虚假实现。
理想情况下,您不要创建与现有类完全匹配的接口 - 相反,消费该接口的类以接口的形式声明其需求。
【讨论】:
【参考方案4】:部分模拟允许您模拟具体类的功能。见:http://www.ayende.com/wiki/Rhino+Mocks+Partial+Mocks.ashx
【讨论】:
恐怕部分模拟并不能完全做到这一点。它们有助于在抽象类中模拟一些未实现的方法,但如果有实现,它仍然会被调用。换句话说,在我的示例中,将 MockRepository.GenerateStub我认为除了使您想要模拟虚拟的任何方法之外,没有其他方法可以做到这一点 - 我相信创建具体类的模拟的方式是通过动态子类化被模拟的具体类,然后覆盖给定的方法具有您在测试中指定的行为,因此这需要一个虚拟方法才能正常工作。
【讨论】:
以上是关于Rhino Mocks 存根和模拟仅适用于接口?的主要内容,如果未能解决你的问题,请参考以下文章
Rhino Mocks - 使用 ref/out 参数模拟集合
无法使用 Rhino Mocks 模拟具有数组参数的构造函数的类