12

关于单元测试,我被告知生产代码中不应包含与测试相关的代码。

好吧,每次我尝试进行单元测试时,我都觉得自己违反了这条规则。

我的程序集内部有一个类,Xyzzy. 我想将它依赖注入另一个类,然后存根它,这样我就可以单独测试另一个类,所以我创建了一个接口,IXyzzy. 哎呀,现在我的生产中的代码实际上只用于测试。更糟糕的是,我有点反对接口是什么(描述实现者可以做什么,而不是它什么)。Xyzzy 的公共接口和 IXyzzy 完全一样,没有其他人(除了存根)实现 IXyzzy。

这对我来说似乎是一件坏事。

我可以创建一个抽象基类或创建我想在 Xyzzy Overridable/上测试的所有公共方法virtual,但这也感觉不对,因为 Xyzzy 不是为继承而设计的,并且从 YAGNI 的角度来看,它永远不会被继承。

创建单实现者接口仅仅是为了测试反模式吗?有更好的选择吗?

4

3 回答 3

6

只为测试编写代码并没有错。这实际上是正常的,就像生产代码包含仅用于调试和生产监控的功能一样。没有明确的理由不允许这样做。代码应该支持应用程序生命周期的所有方面。测试只是生命周期的另一部分。

从这个意义上说,您使用接口的方法是正确的。如果您让生产应用程序的其余部分也使用接口(而不是具体类,尽管只有一个),那么它在架构上是合理的。

我有点反对接口是什么(描述实现者可以做什么,而不是它是什么)

我在这里没有明白你的意思,因为界面确实描述了对象可以做什么。只有一个具体(生产)实现不会破坏此属性。

如果你仔细想想,每个类都有一个广义上的“接口”:所有方法的公共签名都向外部公开了该类支持的接口。.NET 接口是否实现只是一个细节。班级仍然对外界做出同样的承诺。

于 2013-02-20T23:59:04.000 回答
4

根据我的经验,这是非常典型的 .NET 开发,因为方法覆盖是基于选择加入的。如果你想模拟一个依赖,你需要一个接口或一个方法都是虚拟的对象。

在像 Java 这样的语言中,每个方法都是可覆盖的,单一实现接口确实是一种反模式,优秀的开发人员会指出这一点。

继续做你正在做的事情——在我看来,无论你犯了什么罪,都轻而易举地超过了单元测试的好处!

于 2013-02-21T01:20:47.770 回答
3

是的,这是一种反模式。模式将是“在特定上下文中解决常见问题的方法”。但在这种情况下,我们所拥有的是一种解决方法,而不是解决方案。

有问题的问题是需要将要测试的单元与其(某些)依赖项隔离开来,以便在编写单元测试时不必考虑这些依赖项的实现。这个问题的通用和真正的解决方案称为“模拟”,测试编写者可以从模拟的依赖项中指定所需的任何行为。

相反,强制开发人员创建不必要的单独接口,或将方法声明为virtual,只是技术上无法将一个单元与其他单元完全隔离的一种解决方法。

对于 .NET,有几个模拟工具可以提供这种隔离能力,即 TypeMock Isolator、JustMock 和 MS Fakes。其他语言/平台(包括 Java、Ruby 和 Python)有自己的工具,具有类似的表达能力。

于 2013-02-22T12:22:01.767 回答