PowerMock:当你需要 Mock 不可 Mock 的东西
PowerMock 是 Mockito 的扩展,能 Mock 一些 Mockito 原本搞不定的东西:静态方法、私有方法、构造函数。
但 PowerMock 的代价是:慢、侵入性强、对 JDK 版本敏感。不到万不得已,别用它。
什么场景需要 PowerMock
| 场景 | 说明 | Mockito 能搞定吗 |
|---|---|---|
| 静态方法 | System.currentTimeMillis()、Math.random() | ❌ |
| 私有方法 | 想单独测试某个私有方法 | ❌ |
| 构造函数 | new Date() 返回固定时间 | ❌ |
| 枚举 | Mock 枚举值 | ❌ |
能用 Mockito 搞定的,坚决不用 PowerMock。
Maven 依赖
xml
<!-- PowerMock + JUnit 4 -->
<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>注意:PowerMock 目前对 JDK 17+ 支持有限,使用高版本 JDK 的项目需要特别谨慎。
Mock 静态方法
java
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.runner.RunWith;
@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticClass.class) // 指定要 Mock 的静态类
class StaticMethodMockTest {
@Test
void testStaticMethod() {
// Mock 静态方法
PowerMockito.mockStatic(StaticClass.class);
when(StaticClass.getValue()).thenReturn(100);
// 调用
int result = StaticClass.getValue();
// 验证
assertEquals(100, result);
verifyStatic(StaticClass.class, times(1));
}
}Mock 私有方法
java
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
class PrivateMethodMockTest {
@Test
void testPrivateMethod() throws Exception {
MyClass spy = PowerMockito.spy(new MyClass());
// Mock 私有方法
PowerMockito.doReturn("mocked").when(spy, "privateMethod");
// 调用公共方法(内部会调用被 Mock 的私有方法)
String result = spy.publicMethod();
assertEquals("mocked", result);
}
}Mock 构造函数
java
@RunWith(PowerMockRunner.class)
class ConstructorMockTest {
@Test
void testConstructor() throws Exception {
// 创建 mock 实例
MyClass mockInstance = PowerMockito.mock(MyClass.class);
when(mockInstance.getValue()).thenReturn("mocked");
// Mock 构造函数,让 new MyClass() 返回 mock 实例
PowerMockito.whenNew(MyClass.class).withNoArguments().thenReturn(mockInstance);
// 调用 new MyClass()
MyClass obj = new MyClass();
// 验证
assertEquals("mocked", obj.getValue());
PowerMockito.verifyNew(MyClass.class).withNoArguments();
}
}Mock System 类(特殊)
时间相关的测试常用 PowerMock:
java
@RunWith(PowerMockRunner.class)
@PrepareForTest({System.class})
class SystemTimeTest {
@Test
void testWithFixedTime() {
// 让 System.currentTimeMillis() 永远返回固定值
PowerMockito.mockStatic(System.class);
when(System.currentTimeMillis()).thenReturn(0L);
// 被测代码调用 System.currentTimeMillis() 得到 0
long now = System.currentTimeMillis();
assertEquals(0L, now);
}
}PowerMock 的代价
| 问题 | 影响 |
|---|---|
| 慢 | PowerMock 通过字节码修改实现,启动和执行都慢 |
| JDK 版本 | JDK 9+ 需要额外配置 |
| 侵入性 | 测试和被测代码耦合太紧 |
| 维护成本 | 版本更新时容易出现兼容性问题 |
替代方案
| 原本需要 PowerMock 的场景 | 更好的替代 |
|---|---|
| Mock 静态工具类 | 用依赖注入,让调用方传入 Mock 对象 |
| Mock System 时间 | 用 java.time.Clock 或 Mockito.mockStatic()(JDK 8+) |
| Mock 私有方法 | 重新设计,让它变成可测试的公共方法 |
总结
不到万不得已,别用 PowerMock。先想想能不能通过重构代码来避免它。
PowerMock 是药三分毒,能用 Mockito 搞定的,坚决不用。
