Mockito的使用
1,Mockito的介绍
1.1 什么是mock测试?
Mock测试就在测试过程中,对于某些不容易构造(如HttpServletRquest必须在servlet容器中才能构造出来)或者不容易获取比较复杂的对象(如JDBC中的ResultSet对象),用一个虚拟的对象(Mock对象)来创建以便测试的测试方法。
mock最大功能就是把单元测试的耦合分解开,如果代码中依赖了另外一个类或者接口,它能够模拟出来这些依赖,并且验证所调用的依赖的行为。
比如这样的依赖:
当我们测试A类的时候,如果没有mock,我们把所有的依赖都构建出来,而使用mock的话,就可以将结构分解开,像下面这样:
mock对象使用的范畴:
真实对象具有不可确定的行为,产生不可预测的效果
真实对象很难被创建的
真实对象的某些行为很难被触发
真实对象实际上还不存在的
1.2 Mockito的介绍
Mockito是一个基于Java开发的模拟测试框架,通过Mockito我们可以创建和配置Mock对象,进而简化有外部依赖的类的测试。
使用Mockito的大致流程:
创建外部依赖的Mock对象,然后将此Mock对象注入到测试类中
执行测试代码
校验测试代码是否执行正确
2 ,Mockito的使用
2.1 Mockito环境的搭建
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
2.2 使用Mockito创建mock对象
Mockito提供几种创建mock对象的方式:
1,使用静态方法 mock()
2,使用注解 @Mock
@Mock注解实际上是Mockito.mock() 方法的缩写,同样,我们应该只在测试类中使用它。
我们可以使用 @MockBean
注解将 mock对象添加到Spring的上下文中。
@Mock 将替换Spring上下文中任何相同类型的现有bean,如果没有定义相同类型的bean,将添加一个新的bean。
@MockBean 注解在集成测试中很有用,在集成测试中需要模拟特定的bean,例如外部的service。
下面使用一段代码详细讲解:
@RunWith(SpringRunner.class)
public class MockBeanAnnotationTest {
//mock对象
@MockBean
protected UserRepository mockRepository;
@Autowired
protected ApplicationContext context;
@Test
public void testMockBeanAnnotation() {
// 指定mock对象的行为
Mockito.when(mockRepository.count()).thenReturn(123L);
// 通过应用程序上下文获取Mock的对象
// 也可以直接调用mockRepository.count()方法
UserRepository userRepoFromContext = context.getBean(UserRepository.class);
long userCount = userRepoFromContext.count();
Assert.assertEquals(123L, userCount);
// 验证被调用的就是Mock的对象
Mockito.verify(mockRepository).count();
}
}
2.3 when thenReturn'
的使用
简单来说,when thenReturn 就是指定mock对象的某个方法,让它返回特定的值。
//先创建一个mock对象
UserService userService = Mockito.mock(UserService.class);
//当调用UserService对象的queryUser()方法并且传入任意参数时,指定返回一个User
Mockito.when(userService.queryUser(Mockito.any())).thenReturn(new User("001","张三",18));
//当调用UserService对象的queryUser()方法并且传入 "oo1" 时,指定返回一个User
Mockito.when(userService.queryUser("001")).thenReturn(new User("001","张三",18));
//当调用UserService对象的queryUser()方法并且传入任意字符串时,指定返回一个User
Mockito.when(userService.queryUser(Mockito.anyString())).thenReturn(new User("001","张三",18));
指定方法返回特定值就介绍到这,一般能用到的也就这么多。
2.4 验证方法的调用--verify
下面我们根据例子来讲解 verify
的用法
//先创建一个mock对象
UserService userService = Mockito.mock(UserService.class);
//验证userService的queryUser()方法是否被调用,并且参数时 "001"
Mockito.verify(userService).queryUser("001");
//上面例子,其实更加准确的说法是:验证userService的queryUser()方法得到了一次调用。相当于:
Mockito.verify(userService, Mockito.times(1)).queryUser("001");
上面例子是验证方法被调用了一次,如果想要验证方法被调用多次,只需要把Mockito.times()方法的参数做相应的更改即可。
//先创建一个mock对象
UserService userService = Mockito.mock(UserService.class);
//验证userService的queryUser()方法是否被调用,并且参数时是任意的
Mockito.verify(userService).queryUser(Mockito.any());
//验证userService的queryUser()方法是否被调用,并且参数时是任意字符串
Mockito.verify(userService).queryUser(Mockito.anyString());
3,知识补充 -- spy
最后介绍一个叫做 spy
的东西,前面讲了mock对象的几个功能,其中包括:指定对象的特定行为。但是你会不会疑问,如果我不指定呢,会发生什么呢?答案就是:如果不指定的话,一个mock对象的所有非void方法都返回默认值:int返回0,boolean返回false,对象类型将返回null,而void方法则是什么都不做。
但是往往想要的效果并不如此,希望:除非指定,否则调用这个对象的默认实现,同时又能拥有验证方法调用的功能,这正好是spy对象所能实现的效果,spy的用法如下:
//假如目标类是这样实现的
public class UserService{
public boolean isAdmin(String userName){
return "admin".equals(userName);
}
}
//测试类的写法
@RunWith(SpringRunner.class)
public class UserServiceSpyTest{
//跟创建mock类似,只不过调用的是spy方法,而不是mock方法。spy的用法
UserService userService = Mockito.spy(UserService.class);
//在默认情况下,spy对象会调用这个类的真实逻辑,并返回相应的返回值,这可以对照上面的真实逻辑
userService.isAdmin("admin"); //true
userService.isAdmin("zhangsan"); //false
//spy对象的方法也可以指定特定的行为
Mockito.when(userService.isAdmin(anyString())).thenReturn(true);
//同样的,可以验证spy对象的方法调用情况
userService.isAdmin("admin");
Mockito.verify(userService).isAdmin("admin"); //pass
}
总之,spy 和 mock 的用法非常相似,唯一的区别就是默认行为不一样:spy对象的方法默认调用真实的逻辑,mock对象的方法则是什么都不做,或者是直接返回默认值。
4,小结
这篇文章介绍了mock的概念和Mockito的使用,可能有很多方法没有讲到,但是简单的开发应该是没问题了,如果想要了解Mockito的更详细的用法可以参考:https://www.vogella.com/tutorials/Mockito/article.html
</pre>