模拟框架与MS假货框架
对于像NMock这样的Mock框架与VS 2011伪造框架的区别有点困惑。 通过MSDN,我了解的是Fakes允许你像RhinoMock或NMock一样嘲笑你的依赖关系,但是方法是不同的,Fakes生成代码来实现这个function,但是Mocks框架不能。 那么我的理解是否正确? 伪装只是另一个模拟框架
你的问题是关于MS Fakes框架是如何与NMock不同的,看起来其他答案已经解决了一些问题,但是这里有更多关于它们是如何相同以及它们有什么不同的信息。 NMock也类似于RhinoMocks和Moq,所以我将它们与NMock分组。
我在NMock / RhinoMocks / Moq和MS Fakes Framework之间看到了3个主要区别:
-
MS虚构框架使用生成的代码,很像Visual Studio以前版本中的访问器,而不是genericstypes。 当你想为依赖项使用假货框架时,你需要将包含依赖项的程序集添加到testing项目的引用中,然后右键单击它来生成testing双打(存根或填充)。 那么当你testing的时候,你实际上正在使用这些生成的类。 NMock使用generics来完成同样的事情(即
IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()
)。 在我看来,MS Fakes框架方法禁止在testing中进行代码导航和重构,因为实际上是针对生成的类而不是您的真实接口。 -
MS假货框架提供存根和痣(垫片),而NMock,RhinoMocks和Moq都提供存根和嘲笑 。 我真的不明白MS的决定不包括嘲笑,我个人不是痣的粉丝,原因如下所述。
-
通过MS假货框架,您可以提供您想要存根的方法的替代实现。 在这些替代实现中,您可以指定返回值并跟踪有关如何或如何调用方法的信息。 使用NMock,RhinoMocks和Moq,您可以生成一个模拟对象,然后使用该对象指定存根的返回值或跟踪交互(是否以及如何调用这些方法)。 我发现MS假货的方法更复杂,expression能力更差。
为了澄清框架提供的差异:NMock,RhinoMocks和Moq都提供了两种types的testing(stub和mock)。 假货框架提供存根和痣(他们称之为垫片),不幸的是不包括嘲笑。 为了理解NMock和MS Fakes之间的差异和相似之处,了解这些不同types的testing双打是什么是有帮助的:
存根(stub):存根(stub)用于需要提供方法或属性的值,这些方法或属性将被testing方法要求testing双打。 例如,当我testing的方法调用IStudentRepositorytestingdouble的DoesStudentExist()方法时,我希望它返回true。
在NMock和MS假的存根的想法是相同的,但与NMock你会做这样的事情:
Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));
与MSFakes你会做这样的事情:
IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes. { DoesStudentExistInt32 = (studentId) => { return new Student(); } };
注意,在MS Fakes示例中,您为DoesStudentExist方法创build了一个全新的实现(请注意,它被称为DoesStudentExistInt32,因为假造框架在生成存根对象时将方法名称附加到参数数据types,我认为这样会掩盖testing)。 老实说,NMock的实现也使我感到困惑,因为它使用一个string来标识方法的名字。 (如果我误解了NMock是如何使用的话,请原谅我。)这种方法真的抑制了重构,因此我强烈推荐RhinoMocks或Moq。
Mocks: Mock用于validation被测方法和其依赖关系之间的交互。 使用NMock,您可以通过设置与此类似的期望来实现这一点:
Expect.Once.On(mockStudentRepository).Method("Find").With(123);
这就是为什么我更喜欢RhinoMocks和Moq而不是NMock的原因,NMock使用了较老的期望风格,而RhinoMocks和Moq都支持Arrange / Act / Assert方法,在这种方式中,您将期望的交互作为断言在testing结束时指定为这样:
stubStudentRepository.AssertWasCalled( x => x.Find(123));
再次注意,RhinoMocks使用lambda来代替string来识别方法。 ms假货框架根本不提供嘲笑。 这意味着在你的存根(stubbed)实现中(见上面存根的描述),你必须设置你后来validation的variables是否设置正确。 这看起来像这样:
bool wasFindCalled = false; IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() { DoesStudentExistInt32 = (studentId) => { wasFindCalled = true; return new Student(); } }; classUnderTest.MethodUnderTest(); Assert.IsTrue(wasFindCalled);
我觉得这个方法有些复杂,因为你必须跟踪存根中的调用,然后在testing中稍后再声明它。 我发现NMock,特别是RhinoMocks,更具有performance力的例子。
鼹鼠(Shims):坦率地说,我不喜欢痣,因为它们有可能被误用。 我非常喜欢unit testing(特别是TDD),其中一件事情就是testing代码可以帮助你理解编写糟糕代码的地方。 这是因为testing写得不好的代码很困难。 当使用痣时,这是不正确的,因为痣实际上devise为允许您testing未注入的依赖性或testing私有方法。 他们工作类似于存根,除了你使用ShimsContext像这样:
using (ShimsContext.Create()) { System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); }; }
我对垫片的担心是,人们会开始把它们看作是“一种更简单的unit testing方法”,因为它不会强制你按照你应该的方式编写代码。 有关这个概念的更完整的文章,请参阅我的这篇文章:
有关与假货框架相关的一些问题的更多信息,请看这些post:
如果你有兴趣学习RhinoMocks,这是一个Pluralsight培训video(全面披露 – 我写了这个课程,并获得了付费版税的意见,但我认为这适用于这个讨论,所以我在这里包括它):
你是对的,但还有更多的故事。 这个答案中最重要的事情是:
-
你的架构应该正确使用存根和dependency injection,而不是依靠虚假和嘲讽的拐杖
-
假冒和嘲笑对于testing你不应该或者不能改变的代码是有用的,例如:
- 传统代码不能使用(或有效使用)存根
- 第三方API
- 您没有源代码的资源
垫片 (在开发过程中被称为“鼹鼠”)的确是一个嘲弄的框架,通过绕道调用来运作。 而不是辛苦地build立一个模拟(是的,即使使用Moq是相当痛苦!),垫片只是使用已经到位的生产代码对象。 垫片只是将来自生产目标的呼叫重新路由到testing代表。
存根由目标项目中的接口生成。 Stub对象就是这个接口的一个实现。 使用存根types的好处在于,您可以快速生成存根,而不会使用许多一次性存根混淆testing项目,更不用说浪费时间来创build它们了。 当然,你仍然应该创build混凝土桩,以便在许多testing中使用。
有效实施Fummy(Shims,Mocks和Stubtypes)需要一些习惯,但是非常值得。 通过使用Shims / Mole,Mocks和Stubtypes,我个人节省了数周的开发时间。 我希望你能像我一样拥有和技术一样的乐趣!
据我了解,Visual Studio团队希望避免与可用于.NET的各种模拟库竞争。 MS经常面临这样的艰难决定。 如果他们没有提供某些function(“MS为什么不给我们提供一个模拟库,嘲笑是一个常见的要求?”),他们会受到阻碍,如果他们这样做,该死的(“为什么微软的行为如此激进,自然的支持者退出市场吗?“)他们经常(但并非总是)决定从简单地提供自己的替代品到现有的和广受欢迎的技术。 这似乎是这种情况。
Fakes的垫片function真的非常有用。 当然,有危险。 这需要一些纪律,以确保您只在必要时使用。 但是,它填补了一个很大的差距。 我主要的抱怨是它只能在VS 2012的最终版本中提供,因此只能在.NET开发社区的一个子版本中使用。 太遗憾了。
假货包括两种不同的“假”对象。 第一个被称为“存根(stub)”的本质是一个自动生成的虚拟(dummy),其默认行为可以(通常会)被覆盖以使其成为一个更有趣的模拟。 但是,它确实缺乏大多数当前可用的嘲讽框架提供的一些function。 例如,如果要检查存根实例上的某个方法是否已被调用,则需要自行为其添加逻辑。 基本上,如果你现在手动编写自己的模拟,存根可能看起来像是一个改进。 但是,如果您已经使用了function更全面的模拟框架,您可能会觉得Fakes存根中缺less一些重要的部分。
Fakes提供的另一种types的对象称为“垫片(shim)”,揭示了一种机制来替代那些还没有(或不能)通过模拟进行标准replace的依赖关系的行为。 AFAIK,TypeMock是目前提供这种function的主要模拟框架中唯一的一个。
顺便说一句,如果你以前试过鼹鼠,Fakes基本上是一样的东西,最终从微软研究院走出来,成为一个实际的产品。
对于假(Shim + Stub)对象,上面已经有了很好的定义,尽pipe我觉得最后一段评论的最后一段总结得很好。
虽然很多人会认为Fake(Shim + Stub)对象在某些unit testing用例中是很好的资产,但缺点是无论您使用的是Visual Studio 2012还是Visual Studio 2013,这些选项都是可用的与高级或最终版本。 IOW,这意味着你不会在任何Pro版本上运行任何这些Fakes(Shim + Stub)。
你可能会在Pro版本中看到Fakes(Shim + Stub)菜单选项,但是要注意,有一些相当强的机会,你将会完全没有任何东西……它不会产生任何编译错误,告诉你一些重要的东西缺less,选项只是不存在,所以不要浪费你的时间…
这是开发团队需要考虑的一个重要因素,特别是如果只有一个人使用Ultimate版本,而其他人都使用Pro版本。另一方面,无论您使用哪个Visual Studio版本,Moq都可以通过Nuget轻松安装。 使用Moq没有问题,使用任何工具的关键是要知道它们的用途以及如何正确使用它们;)