1. 概述
单元测试中的一个挑战是mock private方法。本文我们将学习如何使用PowerMock
库来实现这一目标,它得到了JUnit和TestNG的支持。
PowerMock
与EasyMock和Mockito等mock框架集成,并为其增加了额外功能,如模拟private方法、final类和final方法等。
它通过依赖字节码操作和独立的ClassLoader来实现这一点。
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方法:
public int getLuckyNumber(String name) {
saveIntoDatabase(name);
if (name == null) {
return getDefaultLuckyNumber();
}
return getComputedLuckyNumber(name.length());
}
其中 getDefaultLuckyNumber
、 getComputedLuckyNumber
和 saveIntoDatabase
是下面我们将要 mock 的 private 方法。
4. Mock 私有方法
4.1. Mock无参数但有返回值的方法
首先对 getDefaultLuckyNumber 方法进行 mock,这是一个无参私有方法。我们使其返回值为 300:
LuckyNumberGenerator mock = spy(new LuckyNumberGenerator());
when(mock, "getDefaultLuckyNumber").thenReturn(300);
4.2. Mock有参数和返回值的方法
然后对 getComputedLuckyNumber 进行 mock,它带有一个参数:
LuckyNumberGenerator mock = spy(new LuckyNumberGenerator());
doReturn(1).when(mock, "getComputedLuckyNumber", ArgumentMatchers.anyInt());
这里,我们固定返回1。另外,我们并不关心输入参数,使用ArgumentMatchers.anyInt()
作为通配符。
4.3. 验证方法调用
验证private方法是否被调用:
LuckyNumberGenerator mock = spy(new LuckyNumberGenerator());
int result = mock.getLuckyNumber("Tyranosorous");
verifyPrivate(mock).invoke("saveIntoDatabase", ArgumentMatchers.anyString());
5. 警告
最后,虽然可以使用PowerMock测试私有方法,但我们必须谨慎对待这种技术。
考虑到我们的测试目的是验证类的行为,我们应该避免在单元测试期间改变类的内部行为。
模拟技术应该应用于类的外部依赖,而不是类本身。
如果对私有方法的模拟对于测试我们的类至关重要,通常意味着设计不佳。
6. 总结
在这篇简短的文章中,我们展示了如何使用PowerMock扩展Mockito的功能,以在测试的类中模拟和验证私有方法。
这个教程的源代码可以在GitHub上找到。