提升单元测试体验的利器--Mockito使用总结
为神马要使用Mockito?
在编写单元测试的时候,为了尽可能的保证隔离性,我们时常需要对某些不容易构造或者不容易获取或者对外部环境有依赖的对象,用一个虚拟的对象来创建以便于测试.假设你正在开发的的代码中使用到了公司其他部门的接口(通过RPC服务),当编写单元测试的时候你可能为了不让接口真的去调用rpc服务而mock一个接口的对象,最原始的方式是自己手工编写一个该接口的实现类,并且在单元测试的时候注入这个对象,而使用Mockito则可以让我们方便地创建和配置mock对象,使用mockito可以简化对外部环境的依赖.
创建mock对象
这里我们以一个操作Redis的工具类来举例,下面是代码:
public class RedisUtil {
@Autowired
private RedisOperations<String, String> redisTemplate;
public boolean checkKeyExists(String key) {
return redisTemplate.hasKey(key);
}
public void setValueByKey(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
public String getValueByKey(String key) {
return redisTemplate.opsForValue().get(key);
}
public List<String> getMutiValuesByList(List<String> keys){
return redisTemplate.opsForValue().multiGet(keys);
}
public void deleteKey(String key) {
redisTemplate.delete(key);
}
public void increValue(String key,Long count){
redisTemplate.opsForValue().increment(key,count);
}
}
有两种方式可以方便的创建mock对象,第一种方式是
Mockito.mock(RedisUtil.class);
还有一种方式在注入时使用@mock注解:
@mock
private RedisUtil redisUtil;
Tips:如果在代码中频繁的使用Mockito比较烦,可以静态导入package,以下例子全部默认已静态导入Mockito包:
import static org.mockito.Mockito.*;
为测试函数打桩的常用方法
当对象被创建之后,就可以对代码中出现的方法进行自定义的交互,mock对象会记住这些交互,在单元测试的执行中碰到代码中的对应方法会默认执行被你自定义的方法内容.
还是以RedisUtil为例,对方法设定返回值:
when(redisUtil.getValueByKey("key1")).thenReturn("value1");
when(redisUtil.getValueByKey("key2")).thenReturn("value2");
对方法设定返回自定义的异常信息:
when(redisUtil.getValueByKey("key1")).thenThrow(new RuntimeException);
此外Mockito还支持迭代风格的返回值定义:
when(redisUtil.getValueByKey("key1")).thenReturn("value1").thenReturn("Value2");
即当方法第一次调用redis.getValueBykey("key1")时会返回value1,当再次被调用时则会返回value2.这里需要注意的是,当后续再出现调用的时候返回值都会是value2,而且这种迭代风格的定义支持return和Throw的混搭,即你可以控制在函数调用的第一次去抛出一个异常,而在函数调用的第二次绘制一个正常的值.
Mockito如何mock返回值为void的方法
首先,测试中对于返回值为void 的方法进行mock本身是没有什么效果的,Mockito有一个doNothing方法是void方法的默认返回:
doNothing().when(redisUtil).increValue(“key1",1L);
其实这里使用doNothing来mock这个方法并没有什么意义,因为我们mock一个方法的目的无非有两个,第一,在某一中输入环境中模拟返回我们期待的返回值,第二就是当方法抛出异常时能够在我们预期控制之下而不会导致单元测试失败,因此对于返回值为void的方法,我们一般可以不去mock它或者使用doThrow()来为void函数打一个桩,当出现异常的时候mock他的异常返回,当不会有异常发生时,只需要在调用后,verify()一下,验证方法的被调用次数即可.
verify(redisUtil,times(1)).increValue("key1",1L);
代码中的times(1)表示一次,即代码中increValue()返回被调用一次的时候能够通过,还可以支持更加广泛的定义,
never():表示从未被调用
atleastOnce():表示至少被调用一次
atleast(3):表示至少被调用3次
atMost(7):表示最多被调用7次
参数匹配器
这里主要介绍一下内置的几个参数匹配器,其实也很好理解,还是那上面的redisUtil为例,对于redisUtil.getValueByKey来说,我希望对于任意的key都返回同一个值,那就可以这么写:
when(redisUtil.getValueByKey(anyString()).thenReturn("value1")
这样在单元测试过程中,对于任意的输入参数,该方法都会返回value1,相同的类型还有很多anyLong(),anyInt(),anyList()等等
使用Spy对象来监控真实对象
以上所讲的对象都是mock对象,mock对象只能调用打桩方法,不能调用真实方法,使用Spy可以让我们能够监视一个真实对象,既可以对这个对象的某一个函数打桩返回我们期望的值,也可以去调用真实的方法,创建spy对象的方式和mock类似,不同的一点是spy需要传一个真实对象而不是一个CLass对象.这里以一个List为例,
List spy = spy(new LinkedList());
when(spy.get(0)).thenReturn("value1");
doReturn("value2").when(spy).get(0);
上面第二行代码,调用when(spy.get(0)),会去调用真实的方法,会抛出异常,第三行代码则不会去调用真实方法,而返回value2.所以总结一下就是,当使用when去模拟返回值的时候,真是方法会被调用,而是用doReturn()去设置的话,则不会去执行真实方法.
需要注意在使用时应该尽量避免使用spy.
参考文档
提升单元测试体验的利器--Mockito使用总结的更多相关文章
- Atitit.hybrid混合型应用 浏览器插件,控件的实现方式 浏览器运行本地程序的解决方案大的总结---提升用户体验and开发效率..
Atitit.hybrid混合型应用 浏览器插件,控件的实现方式 浏览器运行本地程序的解决方案大的总结---提升用户体验and开发效率.. 1. hybrid App 1 1.1. Hybrid Ap ...
- paip.提升用户体验--radio图片选择器 easyui 实现..
#paip.提升用户体验--radio图片选择器 easyui 实现.. =================================== ##原因... ------------------- ...
- paip.提升用户体验--提升java的热部署热更新能力
paip.提升用户体验--提升java的热部署热更新能力 想让java做到php那么好的热部署能力 "fix online"/在线修复吗??直接在服务器上修改源码生效,无需重启应 ...
- paip.提升用户体验----gcc c++ JIT-debugging 技术
paip.提升用户体验----gcc c++ JIT-debugging 技术 作者Attilax , EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http ...
- paip.提升用户体验-----c++ gcc 命令在notepad++扩展中的配置..
paip.提升用户体验-----c++ gcc 命令在notepad++扩展中的配置.. 作者Attilax , EMAIL:1466519819@qq.com 来源:attilax的专栏 地址: ...
- atitit.提升研发效率的利器---重型框架与类库的差别与设计原则
atitit.提升研发效率的利器---重型框架与类库的差别与设计原则 1. 框架的意义---设计的复用 1 1.1. 重型框架就是it界的重武器. 1 2. 框架 VS. 库 可视化图形化 1 2.1 ...
- paip.提升用户体验---c++ qt 取消gcc编译的警告信息.txt
paip.提升用户体验---c++ qt 取消gcc编译的警告信息.txt 作者Attilax , EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http:// ...
- paip.提升用户体验---论文本编辑器的色彩方案
paip.提升用户体验---论文本编辑器的色彩方案 作者Attilax , EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.ne ...
- paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制
源地址:http://blog.csdn.net/attilax/article/details/12343625 paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制 效果图: ...
随机推荐
- [.NET产品源码保护].NET防止反编译(非混淆加密)
.NET产品源码保护产生的背景: .NET源码加密方案支持C#及VB.NET等语言开发的ASP.NET及WINFORM应用.利用.NET支持托管代码与非托管代码共存的特性,将C#代码经过处理放于非托管 ...
- UITableView grouped样式使用探索
UITableView的style有plain和grouped两种样式,两种样式各有不同的风格和功能,plain样式已经封装好了悬停功能,gouped样式则为我们在区头和区尾在实际项目开发中需要我们选 ...
- 【珍藏】linux 同步IO: sync、fsync与fdatasync
传统的UNIX实现在内核中设有缓冲区高速缓存或页面高速缓存,大多数磁盘I/O都通过缓冲进行.当将数据写入文件时,内核通常先将该数据复制到其中一个缓冲区中,如果该缓冲区尚未写满,则并不将其排入输出队列, ...
- PHP获取Post的原始数据方法小结(POST无变量名)
From : http://blog.csdn.net/hotdigger/article/details/6456240 一般我们都用$_POST或$_REQUEST两个预定义变量来接收POST ...
- ASP.NET Cookie和Session
Cookie和Session C#在服务器,JS在客户端 客户端验证不能代替服务端验证 Http HTTP属于应用层,HTTP 协议一共有五大特点:1.支持客户/服务器模式;2.简单快速;3.灵活;4 ...
- JS判断当前手机类型
window.onload = function () { var u = navigator.userAgent; if (u.indexOf('Android') > -1 || u.ind ...
- Android ContentProvider详解
1.概述 ContentProvider以Uri的形式对外提供数据,允许其他应用程序访问或者修改数据.也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过 ...
- SpringMVC+Spring 事务无法回滚的问题
问题描述: Controller里面执行Service的方法,Service方法抛出异常,但是没有按照事务配置的方式回滚: Service的事务配置没有问题: 出现此问题的原因: 在springmvc ...
- 设计一个神经网络记住Or运算(日志一)
由于公式书写太过麻烦,所以上传设计的手稿过程,为如下:
- 单页应用跳转ui-view,$stateProvider,$urlRouterProvider
<!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta charset="UTF- ...