SpringBoot单测mock前传——Mockit简介

这是我参与新手入门的第2篇文章

大家在写测试的时候可能会发现,我们要测试的某个方法,调用了其他模块的接口,有两种情况让我们的测试寸步难行,一是测试过程中发现所依赖的其他模块还没开发完,二是所依赖的是线上的数据库等模块,不应该在单元测试过程中对数据进行操作,出现上面两种情况时,我们没法调用其他模块的api,就需要对其他模块的接口进行mock。

mock,就是对某些不容易获取或不容易构造的对象,用一个假的对象模拟该对象的行为。Mockit是Java领域常用的Mock库,它可以方便快速的mock一个对象。

Mockit创建mock对象

mockit可以创建两种mock对象:

  1. 通过@Mock注解或静态方法mock创建的对象,其接口被完全模拟
  2. 通过@Spy或静态方法spy创建的对象,其接口被部分模拟,只有被指定模拟方式的接口被模拟,其他接口运行正常

可以通过三种形式创建mock对象:

  1. 静态方法:Mockito.mock(xxxx.class)、Mockito.spy(xxxx)
  2. 注解@Mock、@Spy
  3. spring boot中@MockBean

Mockit mock方法

when()

当我们获得了一个mock对象后,可以对mock对象的方法进行模拟,给定它的返回值或其他行为,主要接口为

  • when(xxx).thenReturn(xxxx)
  • when(xxx).thenThrow(xxxx)
  • when(xxx).thenAnswer(Answer<> a)

示例如下:

public class DbConnect{
  public int getNum(int id);
}

public void mockDemo(){
  DbConnect dbConnect = Mockito.mock(DbConnect.class);
  when(dbConnect(1)).thenReturn(5);
  Assert(5, dbConnect.getNum(1));
}
复制代码

其中较为特殊的是thenAnswer接口,需要我们传入一个Answer回调对象,里面的answer方法指定具体的mock行为,Answer接口如下:

public interface Answer<T> {
    /**
     * @param invocation the invocation on the mock.
     *
     * @return the value to be returned
     *
     * @throws Throwable the throwable to be thrown
     */
    T answer(InvocationOnMock invocation) throws Throwable;
}
复制代码

Java8之后引入了lambda表达式,因此我们可以很方便的写出回调方法,示例如下:

when(mock.someMethod(anyString())).thenAnswer(
    (invocation) -> {
        Object[] args = invocation.getArguments();
        Object mock = invocation.getMock();
        return "called with arguments: " + Arrays.toString(args);
    }
});
  
System.out.println(mock.someMethod("foo"));
复制代码

显然上面的示例会输出"called with arguments: [foo]"

verify()

verify()接口用于检查mock方法以给定参数被调用的次数是否和预期一致

verify(mock, times(5)).someMethod("was called five times");
      
verify(mock, atLeast(2)).someMethod("was called at least two times");
复制代码

Mockit注入mock对象的方式

正如上面所说,当我们要测试A对象的方法时,依赖了B对象的方法,我们使用上述方式创建了mock对象后,需要将B mock对象注入到A对象中,才能对A的方法进行单元测试,和上面提到的创建方式一一对应,Mockit注入对象的方式也有三种

  1. 由静态方法创建的对象,只能通过被注入对象的构造函数,set方法或者反射的方式注入
  2. 如果注解创建的mock对象,可以使用@InjectMocks修饰被注入对象,自动的将mock对象注入到被注入对象的成员变量中
  3. Spring Boot中@MockBean创建的mock对象,将会替换所有的spring容器中的同类型对象,因此,只要使用Spring Boot自动注入即可,不需要额外的配置

反射注入

当我们要对测试对象中的私有成员变量mock,而且没有set等方法进行注入时,只能用反射的方式注入,Spring Boot test提供了一个方便的工具类ReflectionTestUtils,其中有setField()方法,可以对私有变量进行注入

public class A{
	private B b;
}

B b = Mockit.mock(B.class);
A a = new A();
ReflectionTestUtils.setField(a, "b", b);
复制代码

小结

Mockito是Java世界中常用的mock库,它的出现极大的方便了测试代码的编码,接下来我会继续为大家介绍Spring Boot中Controller层和Service层测试的详细细节,欢迎大家继续关注。