PowerMock 注解和使用方法
注解概述
PowerMock 有两个重要的注解:
@RunWith(PowerMockRunner.class)
@PrepareForTest({MyObject.class})
@PrepareForTest
和 @RunWith
注解是结合使用的,不要单独使用它们中的任何一个,否则不起作用。当使用 PowerMock 去 mock 静态、final 或者私有方法时,需要加上这两个注解。
注意:在输入
@RunWith
注解时,IDE(如 Eclipse)可能会自动导入org.powermock.modules.junit4.legacy.PowerMockRunner
包,记得把它换成org.powermock.modules.junit4.PowerMockRunner
,否则会抛出java.lang.NoClassDefFoundError
异常。
普通 Mock
测试目标代码
public boolean callArgumentInstance(File file) {
return file.exists();
}
测试用例代码
@Test
public void testCallArgumentInstance() {
File file = PowerMockito.mock(File.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(file.exists()).thenReturn(true);
Assert.assertTrue(underTest.callArgumentInstance(file));
}
说明:普通 Mock 不需要加
@RunWith
和@PrepareForTest
注解。
Mock 方法调用
测试目标代码
public String getFilePath() {
return path;
}
public String getPayloadName() {
String pathWithName = getFilePath();
try {
int index = pathWithName.lastIndexOf(Constant.SLASH);
return pathWithName.substring(index + 1);
} catch (Exception e) {
return pathWithName;
}
}
测试用例代码
@Test
public void testGetPayloadName() throws Exception {
FileItem item = PowerMockito.mock(FileItem.class);
String filePath = "../../../test/updates/Citrix.ibr";
PowerMockito.when(item.getFilePath()).thenReturn(filePath);
PowerMockito.when(item, "getPayloadName").thenCallRealMethod();
assertEquals("Citrix.ibr", item.getPayloadName());
}
说明:当使用 mock 出来的对象调用某个方法时,要对该方法使用
thenCallRealMethod()
。
whenNew
测试目标代码
public class ClassUnderTest {
public boolean callInternalInstance(String path) {
File file = new File(path);
return file.exists();
}
}
测试用例代码
@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassUnderTest.class)
public class TestClassUnderTest {
@Test
public void testCallInternalInstance() throws Exception {
File file = PowerMockito.mock(File.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.whenNew(File.class).withArguments("bing").thenReturn(file);
PowerMockito.when(file.exists()).thenReturn(true);
Assert.assertTrue(underTest.callInternalInstance("bing"));
}
}
说明:当使用
PowerMockito.whenNew
方法时,必须加上@PrepareForTest
和@RunWith
注解。注解@PrepareForTest
里写的类是需要 mock 的 new 对象代码所在的类。
Mock final 方法
测试目标代码
public class ClassUnderTest {
public boolean callFinalMethod(Dependency d) {
return d.isAlive();
}
}
class Dependency {
public final boolean isAlive() {
return true;
}
}
测试用例代码
@RunWith(PowerMockRunner.class)
@PrepareForTest(Dependency.class)
public class TestClassUnderTest {
@Test
public void testCallFinalMethod() {
Dependency dependency = PowerMockito.mock(Dependency.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(dependency.isAlive()).thenReturn(true);
Assert.assertTrue(underTest.callFinalMethod(dependency));
}
}
说明:当需要 mock final 方法的时候,必须加上
@PrepareForTest
和@RunWith
注解,@PrepareForTest
里写的类是 final 方法所在的类。
Mock 私有方法
测试目标代码
public class ClassUnderTest {
public boolean callPrivateMethod() {
return isAlive();
}
private boolean isAlive() {
return true;
}
}
测试用例代码
@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassUnderTest.class)
public class TestClassUnderTest {
@Test
public void testCallPrivateMethod() throws Exception {
// 创建 ClassUnderTest 实例并使用 spy 包装它
ClassUnderTest underTest = PowerMockito.spy(new ClassUnderTest());
// 使用 PowerMockito.when() 和 doReturn() 来 mock 私有方法
PowerMockito.doReturn(false).when(underTest, "isAlive");
// 验证调用私有方法的结果
assertFalse(underTest.callPrivateMethod());
}
}
说明:与 mock final 方法一样,必须加上
@PrepareForTest
和@RunWith
注解,@PrepareForTest
里写的类是 private 方法所在的类。
Mock 静态方法
测试目标代码
public class ClassUnderTest {
public boolean callStaticMethod(Dependency d) {
return Dependency.isExist();
}
}
class Dependency {
public static boolean isExist() {
return true;
}
}
测试用例代码
@RunWith(PowerMockRunner.class)
@PrepareForTest(Dependency.class)
public class TestClassUnderTest {
@Test
public void testCallStaticMethod() {
PowerMockito.mockStatic(Dependency.class);
ClassUnderTest underTest = new ClassUnderTest();
PowerMockito.when(Dependency.isExist()).thenReturn(true);
Assert.assertTrue(underTest.callStaticMethod(depencency));
}
}
说明:mock 静态方法时需要加上
@PrepareForTest
和@RunWith
注解,@PrepareForTest
注解中的类是静态方法所在的类。
suppress
测试目标代码
public static class Utility {
/**
* 一个模拟的耗时操作。
*
* @return 耗时操作的结果
*/
public static String performExpensiveOperation() {
// 假设这是一个很耗时的操作
return "Expensive operation result";
}
public static String processData() {
// 使用耗时操作
String result = performExpensiveOperation();
return "Processed: " + result;
}
}
测试用例代码
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Utility.class })
public class TestRefreshMgmt {
/**
* Suppress 示例
*/
@Test
public void testProcessDataWithSuppressedMethod() {
// 抑制 Utility 类的静态方法 performExpensiveOperation
PowerMockito.suppress(PowerMockito.method(Utility.class, "performExpensiveOperation"));
// 实例化被测试的类
Utility utility = new Utility();
// 调用需要测试的方法
String result = utility.processData();
// 验证结果,这里因为 performExpensiveOperation 被抑制,result 将不包含真实结果
assertEquals("Processed: null", result);
}
}
说明:
PowerMockito.suppress
方法用于禁用某个域或方法。在本例中,使用 PowerMockito.suppress() 来抑制 performExpensiveOperation() 方法的执行。这样在调用 processData() 时,这个耗时操作将不会执行,返回 null。
WhiteBox
测试目标代码
public class CataElement {
private boolean isAvailable = false;
private List<FileItem> items = new ArrayList<>();
private Date parseDate(String date) {
if (!isAvailable) return null;
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
try {
if (date == null || date.isEmpty()) return null;
return sdf.parse(date);
} catch (ParseException e) {
return null;
}
}
}
测试用例代码
public class CataElementTest {
@Test
public void test() {
CataElement e = new CataElement();
Whitebox.setInternalState(e, "isAvailable", true);
List<FileItem> items = Whitebox.getInternalState(e, "items");
assertTrue(items.size() == 0);
}
}
Whitebox 方法:
Whitebox.setInternalState(object, "fieldName", value)
:设置某个对象的某个字段的值。Whitebox.getInternalState(object, "fieldName")
:获取某个对象的某个字段的值。Whitebox.invokeMethod(object, methodName, para)
:调用私有方法,测试私有方法的返回值。
Answer
测试目标代码
public class EmployeeService {
public Employee findEmployeeByEmail(String email) {
// 模拟的实际业务逻辑
return null;
}
}
public class Employee {
private String name;
private String email;
public Employee() {}
public Employee(String name, String email) {
this.name = name;
this.email = email;
}
// Getter 和 Setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Employee{name='" + name + "', email='" + email + "'}";
}
}
测试用例代码
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest(EmployeeService.class) // PowerMockito 需要准备被 mock 的类
public class EmployeeServiceTest {
@Test
public void testFindEmployeeByEmail() throws Exception {
// 使用 PowerMockito mock EmployeeService 类
EmployeeService mockEmployeeService = PowerMockito.mock(EmployeeService.class);
// 使用 Answer 动态定义 findEmployeeByEmail 的行为
PowerMockito.when(mockEmployeeService.findEmployeeByEmail(Matchers.anyString()))
.thenAnswer(new Answer<Employee>() {
@Override
public Employee answer(InvocationOnMock invocation) throws Throwable {
// 获取传入的 email 参数
String email = (String) invocation.getArguments()[0];
// 根据不同的 email 返回不同的 Employee 对象
if (email == null) {
return null;
} else if (email.startsWith("deep")) {
return new Employee("Deep Employee", email);
} else {
return new Employee("Generic Employee", email);
}
}
});
// 验证模拟行为
Employee deepEmployee = mockEmployeeService.findEmployeeByEmail("deep@test.com");
assertNotNull(deepEmployee);
assertEquals("Deep Employee", deepEmployee.getName());
assertEquals("deep@test.com", deepEmployee.getEmail());
Employee genericEmployee = mockEmployeeService.findEmployeeByEmail("generic@test.com");
assertNotNull(genericEmployee);
assertEquals("Generic Employee", genericEmployee.getName());
assertEquals("generic@test.com", genericEmployee.getEmail());
// 验证传入 null 时的行为
Employee nullEmployee = mockEmployeeService.findEmployeeByEmail(null);
assertNull(nullEmployee);
}
}
说明:改测试方法根据不同的参数返回不同的结果,Answer的泛型类型
必须和answer方法的返回值类型一致。
Answer接口指定执行的action和返回值。Answer的参数是InvocationOnMock的实例,支持:
callRealMethod():调用真正的方法
getArguments():获取所有参数
getMethod():返回mock实例调用的方法
getMock():获取mock实例