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上找到。