一、PowerMock 核心定位
- 用途:扩展Mockito(或EasyMock),解决传统Mock框架无法处理的场景:
- 静态方法、静态块
- 构造函数、私有方法、final类/方法
- 系统类(如
System.currentTimeMillis()
)
- 协作框架:需与Mockito或EasyMock配合使用。
- 适用场景:谨慎使用!优先优化代码设计,而非依赖PowerMock。
二、环境配置(Maven)
1. 添加依赖
<!-- PowerMock + Mockito 组合 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
2. 版本兼容性
- 确保PowerMock与Mockito/JUnit版本匹配(官方兼容表)。
三、核心用法示例
1. 测试类配置
使用 PowerMockRunner
并声明需准备的类:
@RunWith(PowerMockRunner.class) // 必须使用PowerMockRunner
@PrepareForTest({StaticUtils.class, User.class}) // 声明需增强的类(含静态/私有方法)
public class UserServiceTest {
// ...
}
2. Mock静态方法
@Test
public void testStaticMethod() {
// 1. 准备静态类
PowerMockito.mockStatic(StaticUtils.class);
// 2. Stubbing:预设静态方法行为
PowerMockito.when(StaticUtils.getSystemTime()).thenReturn(1630454400000L);
// 3. 调用被测方法(内部依赖StaticUtils.getSystemTime())
long result = userService.getCurrentSystemTime();
// 4. 断言及验证
assertEquals(1630454400000L, result);
PowerMockito.verifyStatic(StaticUtils.class); // 必须验证静态调用
}
3. Mock构造函数
模拟创建对象时的行为:
@Test
public void testConstructor() throws Exception {
// 1. 预设构造函数返回的Mock对象
User mockUser = new User(1, "Mocked");
PowerMockito.whenNew(User.class).withArguments(anyInt(), anyString()).thenReturn(mockUser);
// 2. 调用被测方法(内部执行 new User(1, "Alice"))
User user = userService.createUser(1, "Alice");
// 3. 验证构造逻辑
PowerMockito.verifyNew(User.class).withArguments(1, "Alice");
assertEquals("Mocked", user.getName());
}
4. Mock私有方法
@Test
public void testPrivateMethod() throws Exception {
// 1. 创建被测类的Spy对象(部分真实调用)
UserService spyService = PowerMockito.spy(userService);
// 2. Stubbing:预设私有方法行为
PowerMockito.doReturn("Mocked").when(spyService, "internalGenerateToken", anyInt());
// 3. 调用被测方法(内部调用internalGenerateToken)
String token = spyService.getUserToken(1);
// 4. 验证结果
assertEquals("Mocked", token);
}
5. 绕过Final限制
直接Mock final类:
@PrepareForTest({FinalClass.class}) // 声明final类
public class FinalClassTest {
@Test
public void testFinalClass() {
FinalClass mock = PowerMockito.mock(FinalClass.class);
when(mock.doSomething()).thenReturn("Mocked");
assertEquals("Mocked", mock.doSomething());
}
}
四、高级场景
1. Mock系统类(如System.currentTimeMillis)
@Test
public void testSystemTime() {
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.currentTimeMillis()).thenReturn(1630454400000L);
assertEquals(1630454400000L, System.currentTimeMillis());
}
2. 单例模式Mock
@PrepareForTest({Singleton.class})
public class SingletonTest {
@Test
public void testSingleton() {
Singleton mockInstance = PowerMockito.mock(Singleton.class);
PowerMockito.mockStatic(Singleton.class);
PowerMockito.when(Singleton.getInstance()).thenReturn(mockInstance);
when(mockInstance.doSomething()).thenReturn("Mocked");
// ... 调用并验证
}
}
五、注意事项
- 谨慎使用:PowerMock破坏了代码的可测试性设计,优先考虑重构代码(如将静态方法改为实例方法)。
- 兼容性问题:不支持JUnit 5(需结合JUnit 4),且与某些JDK版本存在兼容风险。
- 测试性能:使用PowerMock的测试类运行较慢,尽量减少其使用范围。
- 明确验证:静态方法、构造函数等需显式调用验证(如
verifyStatic()
)。
六、最佳实践
- 仅用于遗留代码:在新项目中优先优化设计,避免依赖PowerMock。
- 隔离测试:将使用PowerMock的测试类单独放置,防止影响其他测试。
- 最小化使用:只对必要的复杂场景使用,如无法修改的第三方库。