15

多年来,我一直是 EasyMock 的粉丝,多亏了 SO,我发现了对 PowerMock 的引用,它能够模拟构造函数和静态方法,这两种方法都会在对遗留代码库进行测试时引起问题。

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

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

除此之外,关于我的代码被字节码操纵以进行测试的事情并不完全适合我。具体的原因我也说不出来,只是觉得这只是为了测试而不是为了生产,让我感到有些不安。

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

我想我的问题是,人们对这些功能使用 PowerMock(或任何其他类似库)的经验是什么,您会使用它们吗?您希望您的测试在多大程度上影响您的设计?

4

6 回答 6

15

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

没有理由限制设计选择的模拟工具。EasyMock、EasyMock 类扩展、jMock、Mockito 等排除的不仅仅是静态方法。这些工具还阻止您声明类和方法final,仅此一项就是一件非常糟糕的事情。final(如果您需要一个为类和方法的使用辩护的权威来源,请参阅“Effective Java”一书,或观看作者的此演示。)

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

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

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

于 2009-06-29T01:32:16.220 回答
13

我完全同意可测试性不是最终目标,这是我在开发 PowerMock 时意识到的事情之一。我也同意编写单元测试是获得良好设计的一种方式。使用 PowerMock 可能应该是一个例外而不是规则,至少是对构造函数的期望和静态模拟等功能。

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

遗留代码也会出现同样的问题。一些组织非常害怕修改他们现有的代码,在这种情况下,PowerMock 可用于在您目前正在编写的部分中引入单元测试,而无需进行大的重构。

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

PowerMock创始人)

于 2009-01-15T10:17:23.613 回答
5

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

但是,我认为您对字节码检测是错误的。我一直使用mockito来模拟具体的类——它让我不必为每个类编写接口。单身的。类。那干净多了。

只有您可以防止代码异味。

于 2009-01-09T14:11:06.767 回答
4

在 .NET 领域,我们遇到过很多关于 Typemock Isolator 的相同问题。见这篇博文

我认为,当人们开始意识到可测试性不是最终目标,并且设计是通过其他方式学习的,那么我们将不再让我们的恐惧支配我们使用哪些工具,或者在何时以及如果它成为更先进的技术时不使用它相关的。

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

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

于 2009-01-09T21:09:24.020 回答
3

我认为你的担心是对的。一旦你学会了如何重构遗留代码以使其可测试,在大多数情况下并不难。

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

(我刚读到这个,觉得它是相关的。)

于 2009-01-09T13:00:16.877 回答
0

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

  1. 反射依赖于方法/字段等的字符串名称。任何重命名都会破坏测试,然后修复所有通过反射访问此方法的测试。与不需要反射的测试相比,IDE 将重构所有重命名。
  2. Powermock 的 stubbing new、静态方法调用等特性让您了解函数的实现细节。理想情况下,测试应该测试功能,例如。

    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 主要面向具有单元测试专业知识的人。将它放在初级开发人员手中可能弊大于利”。

于 2016-09-25T22:16:21.850 回答