Bootstrap

mock

mock的由来

一开始,没有mock的时候,人们测试的时候只能自己mock。比如需要一个PersonDao类的对象,其中有连接数据库查询数据库当前存储人数的方法getPersonNum()。但是可能因为数据库不存在等问题,其中的操作并不能正确执行,这时候一般:

public PerSonDaoMock extends PersonDao{
    @Override
    public int getPersonNum() {
        return 5;
    } 
}

然后测试的时候新建一个PerSonDaoMock对象,注入到需要PerSonDao的地方,就可以在假设查数据库能正常返回的情况下,测测其他部分的逻辑究竟对不对。

其实一开始并没有mock框架,不过像上面的情况发生的多了,便总结出了mock框架,并发展出了更加强大的功能。

mock

  1. mock对象;
  2. 录制对象行为,否则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

methodmockspy
when…thenReturnny
doReturn…whennn

只有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的假对象的结合体。

;