单个实现者接口是否用于单元测试反模式?

时间:2013-02-20 23:45:04

标签: .net unit-testing interface mocking anti-patterns

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

好吧,每当我尝试进行单元测试时,我都觉得我违反了规则。

我的程序集内部有一个类Xyzzy。我希望将依赖项注入另一个类然后将其存根,以便我可以单独测试另一个类,因此我创建了一个接口IXyzzy。哎呀,现在我的代码在生产中真正只用于测试。更糟糕的是,我有点违背了什么界面(描述了实施者可以做什么,而不是 )。 Xyzzy的公共界面和IXyzzy完全相同,没有其他人(除了存根)实现IXyzzy。

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

我可以创建一个抽象基类,或者创建我想要在Xyzzy Overridable / virtual上测试的所有公共方法,但是由于Xyzzy不是为继承而设计的,因此感觉不对。 YAGNI的观点,永远不会继承。

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

3 个答案:

答案 0 :(得分:6)

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

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

  

我有点违背了什么界面(描述了什么   实施者可以做,而不是它是什么)

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

如果你仔细想想,每个类都有一个松散意义上的“界面”:所有方法的公共签名都暴露了一个类支持外部的接口。是否实现.NET接口只是一个细节。该课程仍向外界作出同样的承诺。

答案 1 :(得分:4)

根据我的经验,这是.NET开发的典型特征,源于方法覆盖是在选择加入的基础上;如果你想模拟一个依赖项,你需要一个接口或一个方法都是虚拟的对象。

在像Java这样的语言中,每个方法都是可覆盖的,单实现接口确实是反模式,好的开发人员会将其调用。

继续做你正在做的事情 - 在我看来,无论你犯下什么罪,都会轻易地超过你单位测试的好处!

答案 2 :(得分:2)

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

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

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

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