1. 概述
使用 Mock 框架进行单元测试早已成为业界共识,而近年来 Mockito 更是凭借简洁易用的 API 成为 Java 社区中首选的 Mock 工具。
然而,为了保持良好的设计和对外 API 的简洁性,Mockito 故意舍弃了一些高级特性。在某些场景下,这种取舍反而让测试变得复杂,甚至需要编写大量冗余代码才能完成 Mock。
✅ 这就是 PowerMock 出场的理由。
PowerMockito 是 PowerMock 针对 Mockito 的扩展 API,它通过封装 Java 反射机制,弥补了 Mockito 的不足,比如无法 Mock final
、static
或 private
方法等。
本文将带你了解 PowerMockito 的核心用法,并展示如何在实际测试中应用。
2. 集成 PowerMockito 到测试环境
要在项目中使用 PowerMockito,首先需要在 Maven 的 pom.xml
中添加以下两个依赖:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
接着,在测试类上添加如下两个注解以启用 PowerMockito:
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.baeldung.powermockito.introduction.*")
其中,@PrepareForTest
注解的 fullyQualifiedNames
属性用于指定需要被 Mock 的类的完整包名。上面的例子表示 PowerMockito 会准备 com.baeldung.powermockito.introduction
包下的所有类以供 Mock 使用。
✅ 现在我们可以开始使用 PowerMockito 的强大能力了。
3. Mock 构造方法和 final 方法
在这一节中,我们将演示如何通过 PowerMockito 拦截 new
操作,返回一个 Mock 实例,进而 Mock final
方法。
首先定义一个包含 final
方法的类:
public class CollaboratorWithFinalMethods {
public final String helloMethod() {
return "Hello World!";
}
}
接着使用 PowerMockito 创建 Mock 实例:
CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);
然后设置构造器的 Mock 行为:
whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);
现在我们通过默认构造器实例化该类,并验证构造器是否被调用:
CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();
接下来,为 final
方法设置返回值:
when(collaborator.helloMethod()).thenReturn("Hello Baeldung!");
执行方法并验证:
String welcome = collaborator.helloMethod();
Mockito.verify(collaborator).helloMethod();
assertEquals("Hello Baeldung!", welcome);
⚠️ 如果你只想 Mock 某个 final
方法而非全部,可以使用 Mockito.spy(T object)
方法,具体见第 5 节。
4. Mock 静态方法
假设我们需要 Mock 一个类中的静态方法,类定义如下:
public class CollaboratorWithStaticMethods {
public static String firstMethod(String name) {
return "Hello " + name + " !";
}
public static String secondMethod() {
return "Hello no one!";
}
public static String thirdMethod() {
return "Hello no one again!";
}
}
首先注册该类以启用静态方法 Mock:
mockStatic(CollaboratorWithStaticMethods.class);
也可以使用 Mockito.spy(Class<T> class)
来只 Mock 某个特定静态方法。
然后设置期望值:
when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
.thenReturn("Hello Baeldung!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");
或者设置异常抛出:
doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();
调用静态方法并验证:
String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");
assertEquals("Hello Baeldung!", firstWelcome);
assertEquals("Hello Baeldung!", secondWelcome);
验证调用次数:
verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();
⚠️ 注意:verifyStatic()
必须紧跟在静态方法调用之前,否则 PowerMockito 无法识别要验证的方法。
最后,验证异常是否正确抛出:
@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
// 其他测试代码
CollaboratorWithStaticMethods.thirdMethod();
}
5. 部分 Mock(Partial Mock)
有时候我们并不需要完全 Mock 一个类,而是只 Mock 其中部分方法。PowerMockito 提供了 spy
方法来支持这种场景。
使用以下类进行演示:
public class CollaboratorForPartialMocking {
public static String staticMethod() {
return "Hello Baeldung!";
}
public final String finalMethod() {
return "Hello Baeldung!";
}
private String privateMethod() {
return "Hello Baeldung!";
}
public String privateMethodCaller() {
return privateMethod() + " Welcome to the Java world.";
}
}
Mock 静态方法
spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");
String returnValue = CollaboratorForPartialMocking.staticMethod();
verifyStatic();
CollaboratorForPartialMocking.staticMethod();
assertEquals("I am a static mock method.", returnValue);
Mock final 方法
CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);
when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();
Mockito.verify(mock).finalMethod();
assertEquals("I am a final mock method.", returnValue);
Mock private 方法
when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();
verifyPrivate(mock).invoke("privateMethod");
assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);
6. 小结
本文介绍了 PowerMockito 的基本用法,展示了如何通过它来弥补 Mockito 在 Mock final
、static
和 private
方法上的不足。
✅ 所有示例代码均可在 GitHub 项目 中找到。