0.0 前言
这是一篇本不想理它,却因网友督促而进行大修的文章(就像白夜对战斗后的牛头人阿姆进行大修一样)。
这是一个因午觉和懒引发的事故,不睡午觉就不会懒,不会有这个事故,不会有这篇文章,不会对它进行大修。
所以,多思考,少百度(百度之前先过脑子);能从上一句话,得出这个结论的人应该不是一般人吧(:。
0.1 过程回溯
- 大概是这样吧, 反正时间太久,忘得差不多了(请让你的眼睛谨慎食用 * _*)。
- 午休后写了一个Mockito的单元测试,Mockito.when 给了一个 null 的结果,不是预期值。
- 没过脑子(也许短暂的过了脑子)直接百度了“Mockito.when”,百度给推荐一篇“Mockito.when空指针异常”的文章(我竟然食用了,竟然解决了问题,这94这篇文章出现的终极原因)。
- 食用百度推荐的文章后,发现文章中的NullPointerException和Mockito没三毛钱关系(可能有一毛钱关系吧),异常出现是null.method(对,就是用null的对象去调用方法)引起的。
- 我竟然从这个上面找到了灵感(哈哈哈……从对Mockito用法的检查转到了对参数的检查),仔仔细细检查参数,然而并没有什么卵用,但这并未让我放弃对参数对象的问题怀疑。
- 不得不使用出了终极杀器 debug, 最终发现某个参数对象没重写 equals和hashCode 方法, 至此问题原因确定。
- 重新实现参数匹配(ArgumentMatcher),至此问题解决(本想重写 equals和hashCode,奈何本宝宝不会)。
- 注:参数匹配用的是精确匹配(Mockito.eq),Mockito.any 对完成单元测试任务很爽,对测试没啥用。
1. 问题
- Mockito单元测试时,实际返回值不是预期值。
- 参数内容相同的情况下,出现如下图的情况,故我怀疑是 ( : 没用对Mockito。
2. 过程
由于是公司代码不方便贴出来,并且业务相对更复杂也不便于理解, 所以下文的代码出自下面的demo。
demo:Mockito PowerMock 的demo示例
2.1 反手就一个百度
百度给推荐一篇“Mockito.when空指针异常”的文章。食用后,发现NullPointerException是null.method引起的。参考下面代码:
// mock的代码
Mockito.when(userService.testMethod(user)).thenReturn(result);
// 被单元测试的方法
public User testMethod(User user) {
-----
this.otherMethod(String userName);
-----
}
// 被测试方法调用的方法
private String otherMethod(String userName) {
return userName.toString(); // 异常出在这, userName 的值是 null。
}
- 虽然文章内容不当,但是本尊获得了灵感,开始排除了mockito使用不当的可能性,转而从参数匹配上找原因。
2.2 换方向检查问题原因
- 看了文章的后,开始从参数匹配上去检查原因。
- 检查了参数内容,没啥问题,但是还是精确匹配不上。
- 看了mock时参数精确匹配是怎么匹配的,发现是equals 不是 ==。
- 再次检查参数对象,发现有个参数对象没重写 equals和hashCode 方法。
@Test // 有返回值的方法匹配, 参数的三类匹配情况
public void test1(){
Map mockMap = Mockito.mock(Map.class);
// 1.精确匹配
Mockito.when(mockMap.get(11)).thenReturn(111); // 基础类型
Mockito.when(mockMap.get(Lists.newArrayList("袁紫霞"))).thenReturn("白玉京"); // 引用类型
TestCase.assertEquals(111, mockMap.get(11));
TestCase.assertEquals("白玉京", mockMap.get(Lists.newArrayList("袁紫霞")));
}
参数对象list的引用地址不同,依然返回预期值。所以精确匹配果然不是==,而是用的 equals 。
3. 解决问题的三种方案
- 重写equals、hashCode方法;
- 模糊匹配;
- 自定义匹配;
本想重写equals、hashCode方法,但是重写不了(分页相关类PageParam出自引入的一个jar中: 公司封装的分页组件中的类);
模糊匹配又达不到测试的效果;
所以采用的第三种自定义匹配解决问题。
代码如下:
@Test // 引用类型参数,未重写equals方法, 解决方法
public void test14(){
long pageNo = 1;
long pageSize = 20;
String name = "白玉京";
User user = new User();
user.setName(name);
PageParam pageParam = PageParam.create(pageNo, pageSize);
// 1.重写equals方法: PageParam未重写equals方法,参数匹配通不过;重写后可通过。
// Mockito.when(jdbcQueryManager.queryForPageList(user, User.class, pageParam))
// .thenReturn(Lists.newArrayList(user));
// 2.模糊匹配:Mockito.any() 或 Mockito.any(class)
// Mockito.when(jdbcQueryManager.queryForPageList(Mockito.eq(user), Mockito.eq(User.class), Mockito.any()))
// .thenReturn(Lists.newArrayList(user));
// 3.自定义匹配
ArgumentMatcher<PageParam> argPage = (page) -> page.getPageNo() == pageParam.getPageNo() && page.getPageSize() == pageParam.getPageSize();
Mockito.when(jdbcQueryManager.queryForPageList(Mockito.eq(user), Mockito.eq(User.class), Mockito.argThat(argPage)))
.thenReturn(Lists.newArrayList(user));
TestCase.assertEquals(Lists.newArrayList(user), userServiceImpl.findByName(pageNo, pageSize, name));
}
4. 总结
- 小心无大错。
- Mockito参数精确匹配,直接检查对象相等(equals)就OK了。
- 实践是检验真理的唯一标准。多思考、多动手,能自己想出来、实践出来的,只要耗费的时间不多,就别百度,脑子才是自己的。
5. 结束语
- 真相也许94谎言,毕竟是一个妈生的,只是不知道他爸是谁。