Mock是什么?
通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,对静态初始化过程的移除等强大的功能。
为什么要用PowerMock?
举个例子:当测试单机应用的时候,直接写Junit单元测试即可,但当涉及到多个服务时,你写好了你的服务,其它服务尚未完成,这时候就需要模拟调用远程服务,也就需要Mock。
Mock的流程
简单来说,模拟测试一共分为4步:数据准备、打桩(Mock)、执行、验证。
数据准备阶段可以为Mock阶段的准备期望值、参数等数据,执行mock对象的方法后,最后进行验证与判断。
@RunWith(PowerMockRunner.class)
@PrepareForTest(PrivatePartialMockingExample.class)
public class PowerMockTest {
@Test
public void demoPrivateMethodMocking() throws Exception {
//数据准备
final String expected = "TEST VALUE";
PrivatePartialMockingExample underTest = PowerMockito.spy(new PrivatePartialMockingExample());
// mock || 打桩
// PowerMockito.when(underTest, nameOfMethodToMock, input).thenReturn(expected);
PowerMockito.doReturn(expected).when(underTest).methodToTest();
// 执行
String toTest = underTest.methodToTest();
// 验证
// doReturn 设置不会执行when()后的method()方法,依旧会获得和期望值一样的结果
assertEquals(expected, toTest);
}
}
public class PrivatePartialMockingExample {
public String methodToTest() {
System.out.println("public Method");
return methodToMock("input");
}
private String methodToMock(String input) {
System.out.println("private Method");
return "REAL VALUE = " + input;
}
}
Mock的细节
@PrepareForTest(IdGenerator.class)
告诉PowerMock为测试准备某些类,放在测试类和单独的测试方法。支持通配符 @PrepareForTest("com.mypackage.*")
mock和spy
使用Mock生成的类,所有方法都不是真实的方法,而且返回值都是NULL。
使用Spy生成的类,所有方法都是真实方法,返回值都是和真实方法一样的。
when和doReturn
when(dao.getOrder()).thenReturn("returened by mock ");
doReturn(expected).when(underTest).methodToTest();
使用when去设置模拟返回值时,它里面的方法(dao.getOrder())会先执行一次。
使用doReturn去设置的话,就不会产生上面的问题,因为有when来进行控制要模拟的方法,所以不会执行原来的方法。
doNothing()和verify()
doNothing() 用于模拟void方法,其实不做任何事。
Mockito.verify() 验证某些方法使用了几次(默认1次),否定则抛出异常
@Test
public void getTotalEmployee() {
EmployeeService service = PowerMockito.mock(EmployeeService.class);
//doNothing() 用于执行void方法,不做任何事
PowerMockito.doNothing().when(service).getTotalEmployee();
service.getTotalEmployee();
//验证某些方法使用了几次(默认1次),否定则抛出异常
Mockito.verify(service,Mockito.times(1)).getTotalEmployee();
}
whenNew和withArguments
whenNew 模拟new行为,并不会真的创建对象,withArguments 传入构造函数的参数,无参使用 withNoArguments()
@RunWith(PowerMockRunner.class)
@PrepareForTest(DirectoryStructure.class)
public class DirectoryStructureTest {
@Test
public void createDirectoryStructureWhenPathDoesntExist() throws Exception {
final String directoryPath = "mocked path";
File directoryMock = PowerMockito.mock(File.class);
// 这就是如何告诉PowerMockito模拟新文件的构造。
PowerMockito.whenNew(File.class).withArguments(directoryPath).thenReturn(directoryMock);
//验证某个行为发生过几次(默认1次)
PowerMockito.verifyNew(File.class,times(0)).withArguments(directoryPath);
}
}
Arguments Matcher
一个作为参数的接口类,可以应对“根据不同参数返回不同值”的场景。
PowerMockito.when(demoService.findNameById(Mockito.argThat(new ArgumentMatcher
() {
@Override
public boolean matches(String s) {
if (s.equals("Jerry")) {
return true;
} else {
return false;
}
}
}))).thenReturn("Marry");
System.out.println(demoService.findNameById("Jerry")); //返回Marry
System.out.println(demoService.findNameById("NotJerry")); //返回null
Answer interface
一个作为参数的接口类,可以应对“根据不同参数返回不同值”的场景。更强大。
PowerMockito.when(demoService.findNameById(Mockito.anyString())).then(new Answer