使用PowerMock或您的测试对您的设计有多大影响?

多年来我一直是EasyMock的粉丝,多亏了SO,我遇到了对PowerMock的引用,并且它能够模拟构造函数和静态方法,这两种方法在将测试转换为遗留代码库时会导致问题。

显然,unit testing(和TDD)的巨大好处之一是它导致(强制?)更清洁的设计,在我看来,PowerMock的引入可能会减损它。 我会看到这主要表现为:

  1. 回到初始化合作者而不是注入它们
  2. 使用静态而不是使方法归协作者所有

除此之外,关于我的代码被操作用于测试的字节码,对我来说并不合适。 我不能真正给出具体的理由,只是因为它只是为了测试而不是为了生产而让我感到有点不安。

在我目前的演出中,我们真的推动unit testing作为人们改进编码实践的一种方式,并且感觉将PowerMock引入等式可能会让人们稍微跳过这一步,所以我不愿意开始使用它。 话虽如此,我真的可以看到在哪里使用它可以减少开始测试类需要完成的重构量。

我想我的问题是,人们使用PowerMock(或任何其他类似的库)获得这些function的经验是什么,您是否会使用它们以及您希望您的测试对您的设计有多大影响?

我认为你是对的。 一旦你学会了如何大多数情况下重构遗留代码是可测试的并不是那么难

最好慢一点,有一个支持性的学习环境,而不是采取捷径和学习坏习惯。

(我只是读了这篇文章 ,觉得这很重要。)

我不得不强烈反对这个问题。

对于限制设计选择的模拟工具没有任何理由。 EasyMock,EasyMock Class Extension,jMock,Mockito等不仅仅是静态方法。 这些工具还会阻止您final声明类和方法,而这仅仅是一件非常糟糕的事情。 (如果您需要一个权威来源来保护类和方法的final的使用,请参阅“Effective Java”一书,或者观看作者的演示文稿 。)

根据我的经验,“初始化合作者而不是注入它们”通常是最好的设计。 如果通过创建从该类实例化的辅助类来分解解决某些复杂问题的类,则可以利用将特定数据安全地传递给这些子对象的function,同时将它们隐藏在客户端代码中(提供了高级操作中使用的完整数据)。 在公共API中公开这样的辅助类违反了信息隐藏原则,破坏了封装并增加了客户端代码的复杂性。

滥用DI导致无状态对象真正应该是有状态的,因为它们几乎总是对特定于业务操作的数据进行操作。 这不仅适用于非公共帮助程序类,也适用于从UI /表示对象调用的公共“业务服务”类。 此类服务类通常是内部代码(对于单个业务应用程序),其本质上不可重用且仅具有少数客户端(通常仅一个),因为这样的代码本质上是域/用例特定的 。 在这种情况下(顺便说一句,非常常见),让UI类直接实例化业务服务类,传递用户通过构造函数提供的数据更有意义。

能够轻松地为这样的代码编写unit testing正是导致我创建JMockit工具包的原因。 我没有考虑遗留代码,而是关于设计的简单性和经济性。 到目前为止我所获得的结果使我确信可测试性确实是两个变量的函数:生产代码的可维护性 ,以及用于测试该代码的模拟工具的局限性。 因此,如果您从模拟工具中删除所有限制,您会得到什么?

我完全同意Testability不是最终目标,这是我在开发PowerMock时已经意识到的事情之一。 我也同意编写unit testing是获得良好设计的一种方法。 使用PowerMock应该是一个例外而不是规则,至少是对构造函数和静态模拟的期望等function。

我们使用PowerMock的主要动机是使用第三方代码来阻止您的代码可测试。 一个很好的选择是使用反腐败层来抽象第三方代码并使其可测试。 但是,有时我认为使用标准API的代码更简洁。 一个很好的例子是Java ME API。 这里充满了防止unit testing的静态方法调用。

遗留代码可能会出现同样的问题。 有些组织非常害怕修改现有代码,在这种情况下,PowerMock可用于在您正在编写的部分中引入unit testing,而不会强制进行大的重构。

我们的问题是指定一组最佳实践规则何时使用PowerMock或不是新手开发者可以遵循的规则。 创建好的设计真的很难,因为PowerMock为您提供了更多选择,也许它对初学者来说变得更难? 我认为一个更有经验的开发人员欣赏有更多的选择。

( PowerMock的创始人)

我认为你是对的 – 如果你需要PowerMock,你可能有臭的代码。 摆脱那些静电。

但是,我认为你对字节码检测错了。 我总是使用mockito模拟出具体的类 – 这使我不必为每个人编写一个接口 单。 上课 。 那更清洁了。

只有你可以防止代码味道。

关于Typemock隔离器,我们在.NET领域出现了许多相同的问题。 请参阅此博客文章

我认为,当人们开始意识到可测试性不是最终目标,并且设计是以其他方式学习的时候,那么我们就会停止让我们的恐惧决定我们使用哪种工具,或者当它成为什么时使用更先进的技术相关。

此外,根据应用需求,能够选择您的设计方式是有意义的。 不要让工具告诉你如何设计 – 它将让你别无选择。

(我在Typemock工作,但曾经反对过它)

Powermock提供的reflection抽象层似乎很有吸引力,但它使测试变得脆弱。 进一步来说:

  1. Reflection依赖于方法/字段等的字符串名称。任何重命名都会破坏测试,然后通过reflection修复访问此方法的所有测试。 与不需要reflection的测试相比,所有重命名都将由IDE重构。
  2. Powermock存储new静态方法调用等function使您可以查看函数的实现细节。 理想情况下,测试应该测试function,例如。

     functionOldImplementation(){ List l = new ArrayList(); } // old implementation changed to new functionNewImplementation(){ List l = new LinkedList(); } 

模拟new ArrayList()调用将破坏上述重构的测试。 运行回归最需要进行测试,如果以这种方式完成,则会失败。

最近发现这篇文章 ,它解决了问题中提到的大多数问题,我想我会分享它。

文章中的一些要点:

  1. 自推出以来,PowerMock + Javaassist与Java7兼容需要1。5年的时间。 以下是PowerMock更改日志的说明:

     Change log 1.5 (2012-12-04) --------------------------- Upgraded to Javassist 3.17.1-GA, this means that PowerMock works in Java 7! 
  2. PowerMock网站说:

“请注意,PowerMock主要面向具有unit testing专业知识的人员。将它放在初级开发人员手中可能会带来更多弊大于利”。