【问题标题】:mockito, when setup mock stub using when/thenReturn it got exceptionmockito,当使用 when/thenReturn 设置模拟存根时出现异常
【发布时间】:2021-07-23 15:39:35
【问题描述】:

有一个类并想模拟/存根一个方法

public class ToBeMocked {
    public List<HttpCookie> mergeList(@NonNull List<HttpCookie> cookies, HttpCookie oneCookie) {
        
        System.out.println("+++ mergeList(), cookies:"+cookies+", oneCookie:"+oneCookie);

        HashMap<String, HttpCookie> map = new HashMap();
        if (oneCookie != null) {
            map.put("A", oneCookie);
        }
        for (HttpCookie cookie : cookies) {  //<=== it crashed at this line
            map.put(cookie.getName(), cookie);
        }

        List<HttpCookie> list = new ArrayList<HttpCookie>();
        for (Map.Entry<String, HttpCookie> entry : map.entrySet()) {
           list.add(entry.getValue());
        }
        return list;
    }
}

测试;

@Test
public void test() {

        List<HttpCookie> aCookieList = new ArrayList<>();
        HttpCookie a1Cookie = new HttpCookie("A1", "a1");
        HttpCookie a2Cookie = new HttpCookie("A2", "a2");
        aCookieList.add(a1Cookie);
        aCookieList.add(a2Cookie); 
        
        HttpCookie bCookie = new HttpCookie("B", "b1");

        List<HttpCookie> fakeCookieList = new ArrayList<>();
        fakeCookieList.add(bCookie);
        fakeCookieList.addAll(aCookieList);

    ToBeMocked theSpy = spy(new ToBeMocked());

    System.out.println("+++ 111 test(), aCookieList:"+aCookieList+", bCookie:"+bCookie);
        
    //when(theSpy.mergeList(any(List.class), any(HttpCookie.class)))
    when(theSpy.mergeList(eq(aCookieList), any(HttpCookie.class))). //<== exception on this
           .thenReturn(fakeCookieList);

    System.out.println("+++ 222 test()");
    // test
    // it would call some other function which internally call the mergeList(aCookieList, bCookie), and expect to generate a list from the stubbed result to use, here just make it simple to be run able to show the problem
    List<HttpCookie> list = theSpy.mergeList(aCookieList, bCookie);
        
    // verify
    assertEquals(list.contains(bCookie), true);        
}

when(theSpy.mergeList(any(List.class), any(HttpCookie.class))).thenReturn(fakeCookieList); 上遇到异常NullPointerException

日志显示两行:

Called loadFromPath(/system/framework/framework-res.apk, true); mode=binary sdk=28
+++ 111 test(), aCookieList:[A1="a1", A2="a2"], bCookie:B="b1"
+++ mergeList(), cookies:null, oneCookie:null

java.lang.NullPointerException

显然,mergeList() 使用空参数执行,并在 for (HttpCookie cookie : cookies)

问题:

认为 when().thenReturn() 只是用于设置存根,也就是说,当使用任何参数(或特定参数)调用 mock 的 mergeList() 时,它应该返回提供的列表。

when().thenReturn() 没有正确的参数吗? 为什么似乎在when().thenReturn()中执行了mergeList()?

【问题讨论】:

    标签: java unit-testing mockito


    【解决方案1】:

    来自 Mockito doc:

    有时使用 when(Object) for 是不可能或不切实际的 刺探间谍。因此,在使用间谍时请考虑 doReturn|Answer|Throw() 系列的存根方法。

    那就试试吧:

      doReturn(fakeCookieList).when(theSpy)
                .mergeList(eq(aCookieList), any(HttpCookie.class));
    

    但总的来说,您在使用此类测试时要测试什么并不太清楚。被测方法为mergeList,同时通过调用doReturn指定其行为(返回值),最后只检查返回值。 (当然,如果真实的代码是完整的)

    【讨论】:

    • Lvoov,谢谢分享!也试过 doReturn() ,同样的问题。这里的函数被大大简化了,只是为了说明问题所在。一般来说,它是存根一个函数,存根导致它使用空参数执行,因此崩溃。
    • 嗯,真的很奇怪。对于我和这段代码的 sn-p,如果我使用 thenReturn 它会抛出 NullPointerException (方法 mergeList 是用空值调用的,所以就像你的一样)和 doReturn 它可以工作。
    • 我又试了一次,效果如你所说,+1。但不知道为什么。
    【解决方案2】:

    @George Lvov 的解决方案有效。

    Here 解释了 doReturn(...) 和 theReturn(...) 之间的区别,尤其是在 spy 中,doReturn(...) 不会进行真正的方法调用。 但还没有找到记录行为差异的地方。

    如果您使用间谍对象(使用@Spy 注释)而不是模拟对象(使用@Mock 注释),这两种方法的行为会有所不同: when(...) thenReturn(...) 在返回指定值之前进行真正的方法调用。所以如果被调用的方法抛出一个异常,你必须处理它/模拟它等等。当然你仍然会得到你的结果(你在 thenReturn(...) 中定义的) doReturn(...) when(...) 根本不调用该方法。

    【讨论】:

      猜你喜欢
      • 2017-07-21
      • 1970-01-01
      • 2018-12-11
      • 1970-01-01
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      相关资源
      最近更新 更多