1. 概述

使用 Mock 框架进行单元测试早已成为业界共识,而近年来 Mockito 更是凭借简洁易用的 API 成为 Java 社区中首选的 Mock 工具。

然而,为了保持良好的设计和对外 API 的简洁性,Mockito 故意舍弃了一些高级特性。在某些场景下,这种取舍反而让测试变得复杂,甚至需要编写大量冗余代码才能完成 Mock。

✅ 这就是 PowerMock 出场的理由。

PowerMockito 是 PowerMock 针对 Mockito 的扩展 API,它通过封装 Java 反射机制,弥补了 Mockito 的不足,比如无法 Mock finalstaticprivate 方法等。

本文将带你了解 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 finalstaticprivate 方法上的不足。

✅ 所有示例代码均可在 GitHub 项目 中找到。



原始标题:Introduction to PowerMockito | Baeldung

« 上一篇: Java周报,122期
» 下一篇: Java周报,123期