PowerMock + Mockito 与单独使用Mockito的比较

63

请问可以简单总结一下,使用PowerMock相较于仅使用Mockito,有哪些增加的功能?

目前我已经发现以下几点:

  • 能够Mock静态、final和私有方法
  • 移除静态初始化器
  • 允许在无需依赖注入的情况下进行Mock - 对我来说这一点不是很清楚。能否详细说明一下?

除此之外,它还有其他的新增功能吗?能否简要总结一下?

同时,在使用PowerMock的过程中,是否需要做出任何牺牲?


1
PowerMock的优越性超过Mockito的一个很好的例子是在回答Mockito Spy - 在调用构造函数之前存根中。 - Michael Osofsky
5个回答

58

我不知道其他好处,但我想解决你的两个子问题(这篇评论太长了):

允许在没有依赖注入的情况下进行mocking——这个对我来说不太清楚。你能详细说明一下吗?

我认为这源自动机维基页面,其中他们描述了一种重构代码的方法,以避免调用静态方法从而使它可测试。为了更具体地说明他们的意思,假设你有以下代码,并且想要测试该方法,模拟静态方法的行为,但不使用powermock:

public class MyClass {
     public void doGetString() {
         ...
         OtherClass.getString(); //It's complex and scary and needs mocking!
         ...
     }
}

一种解决方法是将静态调用放入自己的对象中,然后注入一个可以在测试时进行模拟的对象。例如,不使用其他框架,可以这样实现:

public class MyClass {
     public static class StringGetter {
         public getString() {
             return OtherClass.getString();                 
         }
     }

     private final StringGetter getter;

     //Existing Constructor
     public MyClass() {
         this(new StringGetter());
     }

     //DI Constructor
     MyClass(StringGetter getter) {
         this.getter = getter;
     }

     public void doGetString() {
         ...
         getter.getString();
         ...
     }
}

我的方法的行为与静态调用的行为分离开来了,现在我可以使用 DI 构造函数在测试时轻松注入模拟对象。当然,使用 PowerMock 我可以直接在原地模拟静态方法并运行。

那么使用 PowerMock 的时候需要放弃什么吗?

从物理上来说不需要,但从哲学上来说需要:)。以下是我个人的观点,并且我试图给出很好的理由,但这些都只是个人意见,带着一定的主观性:

使用 PowerMock 的潜在危险在于,为了实现模拟私有和静态方法的功能,它们使用自定义的类加载器(在生产环境中不应该存在)并更改您类的字节码。可以说,在大多数情况下,这不应该成为问题,但如果您考虑一下,如果字节码已经改变,并且某些副作用不再存在,那么您实际上就是测试基于现有类的不同类。这个问题比较学术。

您可以通过编写全面的集成测试和更高级别的测试来缓解这个问题,这些测试不使用 PowerMock。通过这种方式,即使您的单元测试使用 PowerMock,您也可以更加自信地了解对象的行为。

我反对 PowerMock 的另一个观点是,它很容易成为一种依赖。我同意 PowerMock 可以帮助测试使用遗留代码和其他您无法控制的代码编写的代码。但是,当您可以控制需要模拟的类时,应避免使用它。如果您编写了一个包含私有或静态方法的类,需要显式模拟该方法以便测试其他方法,则我的直觉会告诉我:这个方法可能做了太多的事情,应该重构并分解。在项目中已经有了 PowerMock,您可能会被诱惑只是模拟它并继续前进,这将减轻应该鼓励您重构相同代码时的痛苦。是的,有时候由于各种技术和非技术限制,这是不可能的,但最好是解决痛点而不是避免它们:)


4
父母对于使用PowerMock的谨慎建议可以总结为一个简短易懂的版本:如果可能/允许进行重构,则最好进行重构;使用PowerMock可能会阻碍重构。 - Sled
1
"...你实际上是在测试不同的类,尽管基于你现有的类。" -> 这也适用于通过CGLIB创建的模拟子类,它本身使用自定义类加载器;被测试的类没有直接修改,但对模拟方法的调用必须首先经过覆盖方法,其字节码是动态创建的。因此,这个论点真的很薄弱。关于“支撑”论点,我认为你忽略了另一面:阻止使用finalnewstatic意味着你需要牺牲某些有用的设计选择。 - Rogério
1
听起来你假设我在扩展测试类以便测试它?虽然这确实是Mockito的一个特性,但我认为这种情况也会带来代码异味(如果我模拟了类的某些部分来测试其他部分,我的关注点分离可能不太好)。至于我提供给我的被模拟对象,只要它们对我的测试稳定,我就不关心它们的工作方式,我的测试类应该尽可能接近生产环境。 - Charlie
3
  1. 我仍然坚持使用“拐杖”论点。没有任何东西阻止我使用finalnewstatic,但是我认为如果我选择使用这些方法,其中的代码不应该需要模拟。如果它们非常复杂或布满副作用,那么我可能应该考虑将其封装到其他对象中,这样我就可以在测试时注入模拟对象或在实现更改时注入模拟对象。(更不用说像Guice这样的依赖注入框架 - "@Inject是新的新东西" :D)
- Charlie

13

PowerMock 是 Mockito 的扩展,可以模拟静态方法、构造函数、final 类和方法、私有方法,移除静态初始化器等等。


6

Powermock mockito扩展的另一个功能是支持模拟和存根等于和哈希码

与所有powermock功能一样,需谨慎使用,但为特定结果添加(基于值的)相等性可能会有所帮助。


4

PowerMock的另一个功能是我们可以在方法中模拟新对象的构造(MockConstructor)。当我们无法更改要测试的方法的代码时,这是非常有帮助的。


1
为了模拟最终类,我们可以使用 org.mockito.plugins.MockMaker。您需要做的是:
  1. 在您的test/resource文件夹中创建一个名为mockito-extensions的文件夹。
  2. 在其中创建一个名为org.mockito.plugins.MockMaker的文件。
  3. 在该文件中只需一行mock-maker-inline
这将不需要您添加任何新库,因此可以节省一些运行时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接