多年来我一直是EasyMock的粉丝,多亏了SO,我遇到了对PowerMock的引用,并且它能够模拟构造函数和静态方法,这两种方法在将测试转换为遗留代码库时都会导致问题.
显然,单元测试(和TDD)的巨大好处之一是它导致(强制?)更清洁的设计,在我看来,PowerMock的引入可能会减损它.我会看到这主要表现为:
回到初始化合作者而不是注入它们
使用静态而不是使方法归协作者所有
除此之外,关于我的代码被操作用于测试的字节码,对我来说并不合适.我不能真正给出具体的理由,只是因为它只是为了测试而不是为了制作而让我感到有点不安.
在我目前的演出中,我们真的推动单元测试作为人们改进编码实践的一种方式,感觉将PowerMock引入等式可能会让人们稍微跳过这一步,因此我不愿意开始使用它.话虽如此,我真的可以看到在哪里使用它可以减少开始测试类需要完成的重构量.
我想我的问题是,人们使用PowerMock(或任何其他类似的库)获得这些功能的经验是什么,你会使用它们吗?你希望你的测试对你的设计有多大影响?
我不得不强烈反对这个问题.
对于限制设计选择的模拟工具没有任何理由.这不仅仅是EasyMock,EasyMock Class Extension,jMock,Mockito等人所排除的静态方法.这些工具还会阻止您声明类和方法final
,而这仅仅是一件非常糟糕的事情.(如果您需要一个权威来源来保护final
类和方法的使用,请参阅"Effective Java"一书,或者观看作者的演示文稿.)
根据我的经验,"初始化合作者而不是注入它们"通常是最好的设计.如果您分解,通过创建,从这个类实例化的辅助类解决一些复杂的问题一个类时,你可以采取的具体数据安全地传递给这些子对象的能力优势,而在同一时间从客户端代码隐藏它们(这提供了高级操作中使用的完整数据).在公共API中公开这样的帮助程序类违反了信息隐藏原则,破坏了封装并增加了客户端代码的复杂性.
滥用DI导致无状态对象真正应该是有状态的,因为它们几乎总是对特定于业务操作的数据进行操作.这不仅适用于非公共帮助程序类,也适用于从UI /表示对象调用的公共"业务服务"类.此类服务类通常是内部代码(对于单个业务应用程序),其本身不可重用且仅具有少量客户端(通常仅一个),因为此类代码本质上是域/用例特定的.在这种情况下(顺便说一下,非常常见),让UI类直接实例化业务服务类,传递用户通过构造函数提供的数据更有意义.
能够轻松地为这样的代码编写单元测试正是导致我创建JMockit工具包的原因.我没有考虑遗留代码,而是关于设计的简单性和经济性.到目前为止我所获得的结果使我确信可测试性实际上是两个变量的函数:生产代码的可维护性,以及用于测试该代码的模拟工具的局限性.因此,如果您从模拟工具中删除所有限制,您会得到什么?
我完全同意Testability不是最终目标,这是我在开发PowerMock时已经意识到的事情之一.我也同意编写单元测试是获得良好设计的一种方法.使用PowerMock应该是一个例外,而不是一个规则,至少是对构造函数和静态模拟的期望等功能.
我们使用PowerMock的主要动机是使用第三方代码来阻止您的代码可测试.一个很好的选择是使用反腐败层来抽象第三方代码并使其可测试.但是,有时我认为使用标准API的代码更简洁.一个很好的例子是Java ME API.这里充满了静态方法调用,阻止了单元测试.
遗留代码可能会出现同样的问题.有些组织非常害怕修改现有代码,在这种情况下,PowerMock可用于在您正在编写的部分中引入单元测试,而不会强制进行大量重构.
我们的问题是指定何时使用PowerMock的最佳实践规则,以及新手开发者可以遵循的规则.创建好的设计真的很难,因为PowerMock为您提供了更多的选择,也许它对初学者来说变得更难?我认为一个更有经验的开发人员欣赏有更多的选择.
(PowerMock的创始人)