这是我参与新手入门的第2篇文章
大家在写测试的时候可能会发现,我们要测试的某个方法,调用了其他模块的接口,有两种情况让我们的测试寸步难行,一是测试过程中发现所依赖的其他模块还没开发完,二是所依赖的是线上的数据库等模块,不应该在单元测试过程中对数据进行操作,出现上面两种情况时,我们没法调用其他模块的api,就需要对其他模块的接口进行mock。
mock,就是对某些不容易获取或不容易构造的对象,用一个假的对象模拟该对象的行为。Mockit是Java领域常用的Mock库,它可以方便快速的mock一个对象。
Mockit创建mock对象
mockit可以创建两种mock对象:
- 通过@Mock注解或静态方法mock创建的对象,其接口被完全模拟
- 通过@Spy或静态方法spy创建的对象,其接口被部分模拟,只有被指定模拟方式的接口被模拟,其他接口运行正常
可以通过三种形式创建mock对象:
- 静态方法:Mockito.mock(xxxx.class)、Mockito.spy(xxxx)
- 注解@Mock、@Spy
- 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注入对象的方式也有三种
- 由静态方法创建的对象,只能通过被注入对象的构造函数,set方法或者反射的方式注入
- 如果注解创建的mock对象,可以使用@InjectMocks修饰被注入对象,自动的将mock对象注入到被注入对象的成员变量中
- 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中Collector层和Service层测试的详细细节,欢迎大家继续关注。