Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 为什么在单元测试时接口模拟比子类模拟更可取?_Unit Testing_Oop_Mocking - Fatal编程技术网

Unit testing 为什么在单元测试时接口模拟比子类模拟更可取?

Unit testing 为什么在单元测试时接口模拟比子类模拟更可取?,unit-testing,oop,mocking,Unit Testing,Oop,Mocking,当一个类实现一个接口时,很容易进行模拟,但是,您必须为它创建一个接口 您还可以通过子类化和重写来模拟。如果基类提供无参数受保护的构造函数,则子类mock不会与基类构造函数中的更改绑定 乍一看,创建mock的子类化方法似乎比为所有东西创建接口更可取,但显然,大多数人并不是这样做的 那么,为什么基于接口的模拟比基于子类化的模拟被认为是一种更好的实践呢?当您进行子类型化和重写时,您可能会错过重写您应该重写的方法之一,并实际运行“生产”代码,您希望将其与测试隔离开来 当您模拟接口时,必须100%模拟其行

当一个类实现一个接口时,很容易进行模拟,但是,您必须为它创建一个接口

您还可以通过子类化和重写来模拟。如果基类提供无参数受保护的构造函数,则子类mock不会与基类构造函数中的更改绑定

乍一看,创建mock的子类化方法似乎比为所有东西创建接口更可取,但显然,大多数人并不是这样做的


那么,为什么基于接口的模拟比基于子类化的模拟被认为是一种更好的实践呢?

当您进行子类型化和重写时,您可能会错过重写您应该重写的方法之一,并实际运行“生产”代码,您希望将其与测试隔离开来


当您模拟接口时,必须100%模拟其行为。任何没有通过模拟明确说明的内容都将抛出一个异常,并迫使您解决它。

与大多数观点相反,即使您进行测试驱动开发(TDD),您仍然应该遵循良好的设计实践。诀窍在于使API可测试且设计良好,而是一种反馈技术

因此,这不是接口模拟与子类模拟的问题,而是接口与子类的问题

这是一个非常古老的讨论,应该早在1994年就结束了;在这篇文章中,我们学习到,我们应该支持组合而不是继承

关于这个主题有很长的讨论,但简言之,在具有单一继承的语言中,基于继承的API设计是为了沿着其他(但不可预见的)维度约束所有未来的可变性。这种语言中的继承限制太大了。由于所有主要的面向对象语言(Java、C#、Ruby)都有单一继承,您应该看到大多数面向对象的代码库都正确地避免了继承


当一个设计基于组合而不是继承时,如果你使用模拟对象,那么你将模拟接口,而不是基类。

2015年会提出一个奇怪的问题,因为多年来每个模拟库都有一流的模拟类支持。而且,据我所知,至少在Java社区,只有一个jMock开发人员鼓励用户只模拟接口;其他Java模拟工具开发人员并不坚持这一点,我认为如果您不使用模拟库,这并不奇怪。也就是说,我绝对倾向于使用库来模拟具体的类,而不是仅仅为了测试而创建接口。。。“继承继承”不是使用单独的接口,而是简单地从B类对象内部使用A类型的对象,而不是从java继承java。设计时继承了
java.util.Hashtable
类,而该类本应在内部使用
Hashtable
。@Rogério Fair;我的回答中有一些隐含的假设,这些假设本可以更加明确。我对Composition一词的使用有浓厚的兴趣,它指出(第19页)“对象组合是通过对象获取对其他对象的引用在运行时动态定义的。组合要求对象尊重彼此的接口”。当您在一种单一继承语言中始终遵循设计模式中列出的原则时,继承被排除在外,因此必须是接口。是的,但请注意,问题是关于,而不是关于书中使用的“组合重于继承”原则的一般“接口”概念。我并不是说这个原则有什么问题,只是它不需要单独的接口。在我基于Java的示例中,
属性
类可以/应该有一个类型为
哈希表
的私有字段(好吧,
Java.util.Dictionary
,如果遵循本书中的另一个原则,关于“代码到接口,而不是实现”)。@Rogério给定一种继承语言,当您排除继承时,那么除了分离的接口之外,您还将如何实现多态性?当然,您需要为一个接受多个实现的接口编写代码。但我不明白“组合优先于继承”原则在哪里说多态性是必要的。回到我的例子(我在这里会说这是完美的),请注意原则告诉我们,
属性
不应该扩展
哈希表
,而是由
哈希表
组成;不需要多态性或分离接口。模拟库不是这样工作的。除非是严格的模拟,否则它们不会对没有“通过模拟显式声明”的方法抛出异常。但是,每个模拟库都允许用户在严格验证和非严格验证之间进行选择,因此这总是在用户的控制之下。当然,这同样适用于接口和类的模拟。@Rogério但问题不是比较严格验证和非严格验证。这是关于子类化和覆盖你自己(至少就我的理解而言),我认为这是对这个问题的一个明确答案@Mureinik明确指出了子类mock的一些缺点;但问题中没有说明这一点,我相信在大多数情况下,开发人员都会为此使用模拟库。然后在第二段中,它似乎假设了相反的情况(模拟库用于创建实现类),当它说没有“通过模拟显式声明”的方法将抛出异常时;这个“异常抛出”让我想到了stric