1. 概述

在单元测试中,一个常见的难题是如何对私有方法进行模拟(Mock)。

在这篇文章中,我们将介绍如何借助 PowerMock 库来实现这一点。PowerMock 支持与 JUnit 和 TestNG 配合使用。

PowerMock 是对 EasyMock 或 Mockito 等 Mock 框架的增强,它扩展了这些框架的功能,使其可以模拟私有方法、final 类、final 方法等原本无法处理的内容。

它通过字节码操作和自定义类加载器来实现这些高级功能。

2. Maven 依赖

首先,在 pom.xml 中添加 PowerMock 与 Mockito、JUnit 集成所需的依赖:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.7.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

你可以在这里查看最新版本:

3. 示例代码

我们以一个名为 LuckyNumberGenerator 的类为例,该类有一个公共方法用于生成幸运数字:

public int getLuckyNumber(String name) {
    saveIntoDatabase(name);
    if (name == null) {
        return getDefaultLuckyNumber();
    }
    return getComputedLuckyNumber(name.length());
}

4. 私有方法的模拟方式

为了完整地进行单元测试,我们需要能够模拟这些私有方法的行为。

4.1. 无参但有返回值的方法

举个最简单的例子,我们模拟一个没有参数但有返回值的私有方法,并强制其返回特定值:

LuckyNumberGenerator mock = spy(new LuckyNumberGenerator());

when(mock, "getDefaultLuckyNumber").thenReturn(300);

这段代码将 getDefaultLuckyNumber() 方法的返回值设定为 300。

4.2. 带参且有返回值的方法

接下来,我们模拟一个带参数的私有方法:

LuckyNumberGenerator mock = spy(new LuckyNumberGenerator());

doReturn(1).when(mock, "getComputedLuckyNumber", ArgumentMatchers.anyInt());

这里我们将 getComputedLuckyNumber(int) 方法模拟为始终返回 1。

⚠️ 注意:我们并不关心传入的具体参数,因此使用了 ArgumentMatchers.anyInt() 作为通配符。

4.3. 验证私有方法是否被调用

最后一种场景是验证某个私有方法是否被调用:

LuckyNumberGenerator mock = spy(new LuckyNumberGenerator());
int result = mock.getLuckyNumber("Tyranosorous");

verifyPrivate(mock).invoke("saveIntoDatabase", ArgumentMatchers.anyString());

这段代码验证了 saveIntoDatabase(String) 方法是否被成功调用。

5. 注意事项

虽然 PowerMock 可以帮助我们模拟私有方法,但在实际使用时必须格外小心 ❌。

我们的测试目标应该是验证类的行为是否符合预期,而不是去改变类内部的实现逻辑。

正确的 Mock 应该作用于外部依赖,而非类本身的私有方法。

如果你发现必须模拟私有方法才能完成测试,那可能意味着当前的设计存在问题,建议重新审视类的结构。

6. 总结

本文简单介绍了如何利用 PowerMock 扩展 Mockito 的能力,从而实现对私有方法的模拟和验证。

源码可以从 GitHub 获取:https://github.com/eugenp/tutorials/tree/master/testing-modules/powermock


原始标题:Mocking of Private Methods Using PowerMock