首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是否建议模拟混凝土类?

是否建议模拟混凝土类?
EN

Stack Overflow用户
提问于 2012-08-29 17:03:56
回答 5查看 18.9K关注 0票数 37

在模仿框架网站上给出的大多数例子都是模仿界面。假设我现在使用的是NSubstitute,他们所有的模拟示例都是模拟接口。

但在现实中,我看到的是一些开发人员模拟的混凝土类。是否建议模拟混凝土类?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-08-30 06:52:10

从理论上讲,模拟一个具体的类绝对没有问题;我们正在针对一个逻辑接口(而不是关键字interface)进行测试,并且该逻辑接口是由class还是interface提供的并不重要。

在实践中,.NET/C#使得这有点问题。正如您提到的.NET模拟框架,我将假定您受到该框架的限制。

在.NET/C#中,成员在默认情况下是非虚拟的,因此任何基于代理的模拟行为方法(即从类派生,并覆盖所有成员以执行特定于测试的内容)都不会起作用,除非您显式地将成员标记为virtual。这会导致一个问题:您正在使用一个在单元测试中应该是完全安全的模拟类的实例(即,不会运行任何真正的代码),但是除非您已经确保一切都是virtual的,否则您可能会以运行真实代码和模拟代码的混合结束(如果有构造函数逻辑,这可能会特别有问题,构造逻辑总是运行,如果有其他具体的依赖项需要新的依赖关系,则会复合)。

有几种方法可以解决这个问题。

  • 使用interfaces。这是有效的,也是我们在NSubstitute documentation中的建议,但它的缺点是可能会使您的代码库膨胀,因为实际上可能并不需要这些接口。可以说,如果我们在代码中发现了良好的抽象,我们自然会得到整洁、可重用的接口,我们可以对其进行测试。我还没见过这样的结果,但YMMV。:)
  • 勤奋地四处走动,让一切变得虚拟。这样做的一个有争议的缺点是,我们建议所有这些成员都旨在成为我们设计中的扩展点,而我们实际上只是想要改变整个类的行为来进行测试。它也不会停止构造函数逻辑的运行,如果具体的类需要通过类似于FodyVirtuosity add-in重写其他dependencies.
  • Use程序集,它也没有帮助,你可以使用它来将你程序集中的所有类成员修改为虚拟的。
  • 使用了一个基于非代理的模拟库,如TypeMock (付费),JustMock (付费),Microsoft Fakes (需要VS旗舰版/企业版,尽管它的前身是免费的)或Prig (免费+开源)。我相信它们能够模拟类的所有方面,以及静态成员。

针对最后一个想法的一个常见抱怨是,您正在通过“假”接缝进行测试;我们正在超越通常用于扩展代码的机制,以更改代码的行为。需要走出这些机制可能表明我们的设计是僵化的。我理解这一论点,但我见过创建另一个接口的噪音超过好处的情况。我想这是一个意识到潜在的设计问题的问题;如果你不需要来自测试的反馈来突出设计的刚性,那么它们是很好的解决方案。

我要抛出的最后一个想法是在我们的测试中改变单元的大小。通常,我们将单个类作为一个单元。如果我们有许多内聚类作为我们的单元,并让接口作为该组件周围定义良好的边界,那么我们就可以避免模拟尽可能多的类,而只需在更稳定的边界上模拟。这可以使我们的测试变得更加复杂,好处是我们正在测试功能的内聚单元,并鼓励围绕该单元开发可靠的接口。

希望这能有所帮助。

票数 80
EN

Stack Overflow用户

发布于 2013-03-02 02:09:02

更新

3年后,我想承认我改变了主意。

从理论上讲,我仍然不喜欢仅仅为了方便模拟对象的创建而创建接口。在实践中(我使用的是NSubstitute)使用Substitute.For<MyInterface>()比模拟一个有多个参数的真实类要容易得多,例如Substitute.For<MyCLass>(mockedParam1, mockedParam2, mockedParam3),其中每个参数都应该单独模拟。其他潜在问题在NSubstitute documentation中进行了描述

在我们公司,现在推荐的做法是使用接口。

原始答案

如果您不需要创建同一抽象的多个实现,请不要创建接口。由于它的pointed by David Tchepak,你不想膨胀你的代码库的接口,实际上可能并不需要。

来自http://blog.ploeh.dk/2010/12/02/InterfacesAreNotAbstractions.aspx

你是否从你的类中提取接口来实现松散耦合?如果是,你可能会有一个实现它们的1:1 relationship between your interfaces and the concrete classes。这很可能是not a good sign,并违反了Reused Abstractions Principle (RAP)

一个给定接口只有一个实现的是一种代码嗅觉。

如果你的目标是可测试性,我更喜欢David Tchepak's answer above的第二个选项。

然而,我不相信你必须让一切都变得虚拟。只需将您要替换的方法设为虚拟就足够了。我还将在方法声明旁边添加一个注释,说明方法是虚的,这只是为了使它可以替代单元测试模拟。

但是,请注意,替换具体类而不是接口有一些限制。例如,for NSubstitute

注意:不会为类创建递归替换,因为创建和使用类可能会产生不必要的副作用

票数 12
EN

Stack Overflow用户

发布于 2012-08-29 17:11:31

问题是:何乐而不为呢?

我可以想出几个场景,在其中这是有用的,例如:

具体类的实现还没有完成,或者做它的人是不可靠的。因此,我模拟指定的类,并根据它测试代码。

它对模拟执行数据库访问之类的操作的类也很有用。如果您没有测试数据库,那么您可能希望为测试返回始终为常量的值(这可以通过模拟类来轻松实现)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12174304

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档