1. 概述

单元测试中的一个挑战是mock私有方法。在这个教程中,我们将学习如何使用PowerMock库来实现这一目标,它得到了JUnit和TestNG的支持。

PowerMock与EasyMock和Mockito等模拟框架集成,并为其增加了额外功能,如模拟私有方法、最终类和最终方法等。

它通过依赖字节码操作和独立的类加载器来实现这一点。

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. 私有方法模拟的变体

为了对方法进行详尽的单元测试,我们需要mock私有方法的行为。

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());

在这里,我们模拟私有方法并使其返回1。注意,我们并不关心输入参数,使用ArgumentMatchers.anyInt()作为通配符。

4.3. 验证方法调用

我们的最终策略是使用PowerMock来验证私有方法的调用:

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

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

5. 警告

最后,虽然可以使用PowerMock测试私有方法,但我们必须谨慎对待这种技术。

考虑到我们的测试目的是验证类的行为,我们应该避免在单元测试期间改变类的内部行为。

模拟技术应该应用于类的外部依赖,而不是类本身。

如果对私有方法的模拟对于测试我们的类至关重要,通常意味着设计不佳。

6. 总结

在这篇简短的文章中,我们展示了如何使用PowerMock扩展Mockito的功能,以在测试的类中模拟和验证私有方法。

这个教程的源代码可以在GitHub上找到