lannyf
lannyf

Reputation: 11025

mockito, when setup mock stub using when/thenReturn it got exception

having a class and would like to mock/stub one method

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;
    }
}

the test;

@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);        
}

got exception NullPointerException on the when(theSpy.mergeList(any(List.class), any(HttpCookie.class))).thenReturn(fakeCookieList);.

the log shows two lines:

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

Apparently mergeList() got executed with null params, and crashed at for (HttpCookie cookie : cookies)

Question:

thought the when().thenReturn() is just for setting up the stub, which was saying when the mock's mergeList() is called with any params (or with specific params), it should return the list provided.

is the when().thenReturn() not with proper params? why it seems to execute the mergeList() in the when().thenReturn()?

Upvotes: 0

Views: 542

Answers (2)

lannyf
lannyf

Reputation: 11025

@George Lvov's solution works.

Here explains the difference between doReturn(...) and theReturn(...), especially in spy the doReturn(...) will not make the real method call. But have not find where it is documented the behavior difference.

Both approaches behave differently if you use a spied object (annotated with @Spy) instead of a mock (annotated with @Mock): when(...) thenReturn(...) makes a real method call just before the specified value will be returned. So if the called method throws an Exception you have to deal with it / mock it etc. Of course you still get your result (what you define in thenReturn(...)) doReturn(...) when(...) does not call the method at all.

Upvotes: 1

Georgii Lvov
Georgii Lvov

Reputation: 2701

From Mockito doc:

Sometimes it's impossible or impractical to use when(Object) for stubbing spies. Therefore when using spies please consider doReturn|Answer|Throw() family of methods for stubbing.

So try it so:

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

But in general it is not quite clear what you are testing with such a test. The method under test is mergeList, at the same time you specify its behavior(return value) by calling doReturn and only returned value is checked in the end. (Of course, if the real code is fully represented)

Upvotes: 1

Related Questions