Mockito 未返回预期值(Mockito.when空指针异常)

1. 问题:单元测试时,返回值不是预期值,并抛出NullPointerException

这是项目中单元测试时遇到的返回值不是预期值,抛出NullPointerException的问题。 找到并解决问题的过程挺戏剧性的。 问题出现后,由于午休刚醒没精神,直接百度Mockito.when,然后根据提示搜了"Mockito.when空指针异常",找了篇博客,把问题解决了, 最后发现博客是错的。

我的问题参数内容相同的情况下,出现如下图的测试失败:在这里插入图片描述

2. 过程

由于是公司代码不方便贴出来,并且业务相对更复杂也不便于理解, 所以下文的代码出自最近的一篇demo的博客。

demoMockito PowerMock 的demo示例

2.1 NullPointerException问题

什么时候出现空指针异常: null.method()时。 代码中出现了所mock方法的结果去调用方法的代码,mock方法的结果为null,即出现 null.method()的情况。

2.2 mock时,参数精确匹配是用 equals 还是 ==

浏览了下参考博客上的内容,说是地址不对,所以返回不是预期值,我恍然大悟,赶紧回到项目,准备来一顿猛如虎的操作。转念一想,mockito这么low吗!参数内容相同,引用对象的地址不同都搞不定?当然不可能啊,一个成熟的组件,N多人在使用,怎么可能存在这种问题。
带着疑惑,去探索了一下。

    @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 。

2.3 引用类型对象equals方法是否相等,取决于是否重写equals、hashCode

参数内容相同,却匹配不上,只好去检查参数对象的equals方法是否重写。结果不出所料,分页相关对象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);
        // PageParam未重写equals方法,参数匹配通不过
        Mockito.when(jdbcQueryManager.queryForPageList(user, User.class, pageParam)).thenReturn(Lists.newArrayList(user));

        // 解决方法: 1.重写equals方法;2.模糊匹配; 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));
    }

在这里插入图片描述
到这里,我的问题已经确定,只待解决。 仔细回味一下,我参考的博客说问题的原因是地址不同,貌似就不对咯。 难道有什么不了解的点,只好去撸一撸。
NullPointerException出自null.method不用再看;先去检查了equals和hashCode方法,结果是重写了的;最后发现是:参数对象的内容不一致。

2.4 解决方案

既然博客我去撸了,解决方案自然要看。文章中的解决方案是: 参数改为any(),类似:Mockito.when(mockMap.method(Mockito.any()) ,意思是任何参数都匹配通过。 单元测试是通过了, 但是测试的意义又何在! 没有太大的参考价值。
最终找到的解决方案有三个:1.重写equals方法;2.模糊匹配; 3.自定义匹配。
方案1排除:封装好的jar,再去改它的源码不好;
方案2排除:模糊匹配,比如匹配类型,对象参数的属性值失去测试意义;
方案3选择:自定义匹配时,可以重新实现equals的比较。
自定义匹配参考代码: 上一段代码(方法test14()中注释的部分)。

3. 总结

  • 什么时候出现空指针异常: null.method()时。
  • 什么时候出现预期值(精确匹配): 参数相等(equals相等)。
  • 实践是检验真理的唯一标准。应该相信的心态去参考,带着怀疑一切的心态去实践。

:在此,还是非常感谢参考的那边文章。给提供了思路,不然一时半会真检查不到封装好的组件里面去。

猜你喜欢

转载自blog.csdn.net/besto229/article/details/106077901