mock的由来
一开始,没有mock的时候,人们测试的时候只能自己mock。比如需要一个PersonDao类的对象,其中有连接数据库查询数据库当前存储人数的方法getPersonNum()
。但是可能因为数据库不存在等问题,其中的操作并不能正确执行,这时候一般:
public PerSonDaoMock extends PersonDao{
@Override
public int getPersonNum() {
return 5;
}
}
然后测试的时候新建一个PerSonDaoMock
对象,注入到需要PerSonDao的地方,就可以在假设查数据库能正常返回的情况下,测测其他部分的逻辑究竟对不对。
其实一开始并没有mock框架,不过像上面的情况发生的多了,便总结出了mock框架,并发展出了更加强大的功能。
mock
- mock对象;
- 录制对象行为,否则mock的对象没有任何用。
几种情况
- 有返回值用assertEquals(actual, result)之类的,测试void类型的方法只能用verify看看是否调用过;
- 录制对象的行为when().thenReturn(),如果是void方法,用doNothing().when(),木有thenReturn;
- mock局部变量(无参数):类中含有new的对象,而不是注入的对象(当然这种设计貌似违背了依赖注入原则),需要用whenNew().withNoArguments().thenReturn(),或者withAnyArguments()之类的;这种mock需要修改字节码,因此需要@RunWith(PowerMockRunner.class)和@PrepareForTest(含这个对象的类.class);
- mock静态(eg: Utils):直接mockStatic(类.class)即可,不需要像mock对象那样,还要新建对象(eg: Person p = mock(Person.class));
- mock final类:如果类被final修饰了,意味着这个类不能被继承,如果用easyMock,就会失败,因为它是和远古手写mock一样,是通过代理的方式来mock的(即使用继承extends原类,再用新类的方法代替原类中的方法,override)。但是powerMock是修改字节码,所以可以mock,而且和mock非final类没区别;
- mock constructor其实就是mock局部变量,只不过这个局部变量是带构造参数的,whenNew().withArguments(xxx,xxx,xxx,…).thenReturn()即可;
- Answer接口:录制行为的时候,如果不想写多个when().thenReturn(),就需要使用Answer接口;
- PowerMockito.spy(new xxx()):mock出来的对象是假的,如果不录制行为,啥也做不了。想真正的去调用并执行方法,需要使用spy出来的对象;Spy 是一个特别有意思的 API,他能让你 mock 一个对象,并且只 mock 个别方法的行为,保留对某些方法原始的业务逻辑;
用于测试@Autowired注入的三种方法
@InjectMocks
org.springframework.test.util.ReflectionTestUtils:ReflectionTestUtils.setField(Object targetObject, String name, Object value)
org.powermock.reflect:Whitebox.setInternalState(Object object, String fieldName, Object value)
mock private & mock public & mock static
mock private static method
被测代码:
public class Util {
public static String method(){
return anotherMethod();
}
private static String anotherMethod() {
throw new RuntimeException(); // logic was replaced with exception.
}
}
测试代码:
@PrepareForTest(Util.class)
@RunWith(PowerMockRunner.class)
public class UtilTest {
@Test
public void testMethod() throws Exception {
PowerMockito.spy(Util.class);
PowerMockito.doReturn("abc").when(Util.class, "anotherMethod");
String retrieved = Util.method();
Assert.assertNotNull(retrieved);
Assert.assertEquals(retrieved, "abc");
}
}
mock private method
比如XXXService,只改一句:XXXService xxx = PowerMockito.spy(new XXXService())
即可。
和“mock public method与mock static public method的区别是一样的”,一个有实例,一个没有。而private和public的区别在于:
PowerMockito.mockStatic(EmployeeUtils.class);
PowerMockito.when(EmployeeUtils.getEmployeeCount()).thenReturn(10);
使用mock()和mockStatic(),而不是spy()。
invoke private method using reflecttion
Let's say you want to call MyClass.myMethod(int x);
// get & invoke private static method
Method m = MyClass.class.getDeclaredMethod("myMethod", Integer.TYPE);
m.setAccessible(true); // if security settings allow this
Object o = m.invoke(null, 23); // use null if the method is static
// get & invoke private static field
Field field = RenderUtils.class.getDeclaredField("userDefinedWords");
field.setAccessible(true);
Map<String, String> userDefinedWords = (Map<String, String>)field.get(null);
mock vs. spy && doReturn…when vs. when…thenReturn
method | mock | spy |
---|---|---|
when…thenReturn | n | y |
doReturn…when | n | n |
只有when…thenReturn且对象是spy出来的,才会进入方法体执行(也就是stackoverflow上的情况https://stackoverflow.com/questions/28121177/mock-private-method-using-powermockito),否则都不会。
public class Hello {
public(){
private();
}
private(){
third();
}
third(){}
}
对于以上代码,如果直接Hello hello = mock(Hello.class),调用hello.public()是不会有任何反应的,因为没有录制调用public之后会发生什么,想有反应,又不想录制行为,就得Hello hello = spy(new Hello()),spy会深入到每个方法里面去。但是如果只想深入到public,或者public和private,不想再继续深入,就需要录制private或third的行为,spy走到有录制行为的方法,就不再进去了,但是这个录制必须使用doReturn…when。
所以说,spy是new的真对象和mock的假对象的结合体。