我多年来一直是EasyMock的粉丝,多亏了SO,我遇到了对PowerMock的引用,并且它能够模拟构造函数和静态方法,这两种方法在将测试转换为遗留代码库时会导致问题。
显然,单元测试(和TDD)的巨大好处之一是它导致(强制?)更清洁的设计,在我看来,PowerMock的引入可能会减损它。我会看到这主要表现为:
除此之外,关于我的代码被操作用于测试的字节码,对我来说并不合适。我不能真正给出具体的理由,只是因为它只是为了测试而不是为了生产而让我感到有些不安。
在我目前的演出中,我们真的推动单元测试作为人们改进编码实践的一种方式,感觉将PowerMock引入等式可能会让人们稍微跳过这一步,所以我不愿意开始使用它。话虽如此,我真的可以看到在哪里使用它可以减少开始测试一个类需要完成的重构量。
我想我的问题是,人们使用PowerMock(或任何其他类似的库)获得这些功能的经验是什么,您是否会使用它们以及您希望测试对您的设计有多大影响?
答案 0 :(得分:15)
我非常不同意这个问题。
没有理由限制设计选择的模拟工具。这不仅仅是EasyMock,EasyMock Class Extension,jMock,Mockito等人所排除的静态方法。这些工具还会阻止您声明类和方法final
,而这仅仅是一件非常糟糕的事情。 (如果您需要一个权威来源来保护final
类和方法的使用,请参阅“Effective Java”一书,或者从作者那里观看presentation。)
根据我的经验,“初始化合作者而不是注入他们”通常是最佳设计。如果通过创建从该类实例化的辅助类来分解解决某些复杂问题的类,则可以利用将特定数据安全地传递给这些子对象的功能,同时将它们隐藏在客户端代码中(提供了高级操作中使用的完整数据)。在公共API中公开这样的帮助程序类违反了信息隐藏原则,破坏了封装并增加了客户端代码的复杂性。
滥用DI导致无状态对象确实应该是有状态的,因为它们几乎总是对特定于业务操作的数据进行操作。 这不仅适用于非公共帮助程序类,也适用于从UI /表示对象调用的公共“业务服务”类。此类服务类通常是内部代码(对于单个业务应用程序),本质上不可重用且只有少数客户端(通常只有一个),因为此类代码本质上是域/用例特定的。 在这种情况下(顺便说一下,非常常见的情况)让UI类直接实例化业务服务类,传递用户通过构造函数提供的数据更有意义。
能够轻松地为这样的代码编写单元测试正是导致我创建JMockit工具包的原因。我没有考虑遗留代码,而是关于设计的简单性和经济性。到目前为止我所取得的成果使我确信 testability 确实是两个变量的函数:生产代码的可维护性,以及用于测试该代码的模拟工具的局限性。因此,如果您从模拟工具中删除所有限制,您会得到什么?
答案 1 :(得分:13)
我完全同意Testability不是最终目标,这是我在开发PowerMock时已经意识到的事情之一。我也同意编写单元测试是获得良好设计的一种方法。使用PowerMock应该是一个例外,而不是一个规则,至少是对构造函数和静态模拟的期望等功能。
使用PowerMock的主要动机是使用第三方代码阻止您的代码可测试。一个很好的选择是使用anti-corruption-layer抽象第三方代码并使其可测试。但是,有时我认为使用标准API的代码更简洁。一个很好的例子是Java ME API。这里充满了阻止单元测试的静态方法调用。
遗留代码可能会出现同样的问题。有些组织非常害怕修改现有代码,在这种情况下,PowerMock可用于在您正在编写的部分中引入单元测试,而不会强制进行大量重构。
我们的问题是指定何时使用PowerMock的最佳实践规则,以及新手开发者可以遵循的规则。创建好的设计真的很难,因为PowerMock为您提供了更多的选择,也许它对初学者来说变得更难?我认为一个更有经验的开发人员欣赏有更多的选择。
(PowerMock的创始人)
答案 2 :(得分:4)
我认为你是对的 - 如果你需要PowerMock,你可能有臭臭的代码。摆脱那些静电。
但是,我认为你对字节码检测错了。我使用mockito一直模拟出具体的类 - 这使我不必为每个编写一个接口。单。类。那更清洁了。
只有你可以防止代码味道。
答案 3 :(得分:3)
答案 4 :(得分:3)
对于Typemock Isolator,我们在.NET领域出现了许多相同的问题。 见This blog post
我认为,当人们开始意识到可测试性不是最终目标,并且设计是以其他方式学习的时候,那么我们就会停止让我们的恐惧决定我们使用哪些工具,或者不使用更先进的技术如果它变得相关。
此外,根据应用需求选择您的设计方式也很有意义。不要让工具告诉你如何设计 - 它将让你别无选择。
(我在Typemock工作,但曾经反对过它)
答案 5 :(得分:0)
Powermock提供的反射抽象层似乎很有吸引力,但它使测试变得脆弱。更具体地说:
Powermock的存根new
,静态方法调用等功能使您可以查看函数的实现细节。理想情况下,测试应该测试功能,例如。
functionOldImplementation(){
List l = new ArrayList();
}
// old implementation changed to new
functionNewImplementation(){
List l = new LinkedList();
}
模拟new ArrayList()
调用将破坏上述重构的测试。运行回归最需要进行测试,如果这样做就会失败。
最近发现了这个article,它解决了问题中提到的大部分问题,我想这是分享它。
文章中的一些要点:
使PowerMock + Javaassist兼容需要1。5年 Java7自推出以来。以下是PowerMock更改日志中的注释:
Change log 1.5 (2012-12-04)
---------------------------
Upgraded to Javassist 3.17.1-GA, this means that PowerMock works in Java 7!
PowerMock网站说:
"请注意,PowerMock主要面向有专家的人 单元测试知识。把它交给大三学生 开发人员可能会造成更多弊大于利。"。