Mockito:Java - Unfinished stubbing detection

18,563

Solution 1

You are in the third case:

3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

This happens because thenReturn calls method from another mock scrapeS.scrapePost It is hard to say exactly how to fix this because I would need more implementation details but try building the return object before when and it shouldn't be the return from another mock. There is a good explanation here: https://stackoverflow.com/a/26319364/1121883

Here is a small example. You code is similar to play test. In then return you should provide an object instead of the call on the mock like in fix test.

@Test
public void play(){
    A a = mock(A.class);
    B b = mock(B.class);
    when(a.a("input")).thenReturn(b.b("input"));
}

@Test
public void fix(){
    A a = mock(A.class);
    B b = mock(B.class);
    String returnString = "b";
    when(a.a("input")).thenReturn(returnString);
}

static class A{
    String a(String in){
        return "a";
    }
} 

static class B{
    String b(String in){
        return "b";
    }
}

Your code should be something like:

    List<prodItem> retList = new ArrayList<>();
    retList.add(new ProdItem());
    retList.add(new ProdItem());
    when(bService.getProductList("cookie", "6753")).thenReturn(retList);

Solution 2

The (a bit simplified) evaluation order of your code

when(bService.getProductList("cookie", "6753"))
                .thenReturn(
                scrapeS.scrapePost(new String(),
                        new String(),
                        new HashMap<>(),
                        new bService()));

is first:

bService.getProductList("cookie", "6753")

second

when(/*...*/)

third

scrapeS.scrapePost(new String(),
                        new String(),
                        new HashMap<>(),
                        new bService())

.

So while trying to mock bService you use mock scrapeS.

Note that this doesn't make sense at all. Basically you are currently trying to give bService.getProductList an implementation which should use scrapeS. But bService is a mock and therefore has no implementaion.

If you want that the invocations of bService and scrapeS return the same object then store that object into a local variable and use that local variable in the thenReturn clause of both methods.

Object returnValue = /*whatever the return value is*/
when(bService.getProductList("cookie", "6753")).thenReturn(returnValue);
when(scrapeS.scrapePost(new String(), new String(), new HashMap<>(), new bService())).thenReturn(returnValue);
Share:
18,563
L1ghtk3ira
Author by

L1ghtk3ira

I work mostly in front end web development as my main area of expertise. I love CSS and CSS animation as well as learning my new favourite OPEN GL for Android. The good thing about Open GL is it is greatly transferable between different languages. During my down time I like to play video games, currently playing Dishonored and Red Dead Redemption. If you need help with OpenGl or CSS Animation let me know and I would be glad to help out!

Updated on June 04, 2022

Comments

  • L1ghtk3ira
    L1ghtk3ira almost 2 years

    I have been working with Mockito and have run into this error message:

    org.mockito.exceptions.misusing.UnfinishedStubbingException: 
    Unfinished stubbing detected here:
    -> at       com.rbc.rewards.catalogue.service.B2SServicesTest.getProductList_Success(B2SServ icesTest.java:52)
    
    E.g. thenReturn() may be missing.
    Examples of correct stubbing:
      when(mock.isOk()).thenReturn(true);
      when(mock.isOk()).thenThrow(exception);
      doThrow(exception).when(mock).someVoidMethod();
    Hints:
      1. missing thenReturn()
      2. you are trying to stub a final method, you naughty developer!
      3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
    

    I have read in two posts on Stack Overflow about this issue but they do not go into full detail. I believe it has something to do with nesting mocks inside a mocking (from what I read). However I do not see or fully understand by the small snippets people have posted.

    My test class is as followed (leaving out unnecessary code):

    // Mock:  uses Java Reflection in order to create mock objects of each class
    @Mock
    private Scrapes scrapeS;
    @Mock
    private SsoS ssoS;
    @Mock
    private BScrape bScrape;
    
    //@InjectMocks annotation is used to create and inject the mock object
    @Mock
    private BService bService;
    
    
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }
    
    // Testing:
    @Test
    public void getProductList_Success() throws Exception{
    
            when(BService.getProductList("cookie", "6753"))
                    .thenReturn(
                    scrapeS.scrapePost(new String(),
                            new String(),
                            new HashMap<>(),
                            new bService()));
    }
    

    Method I need to call:

    public List<prodItem> getProdList(String raw_cookie, String catId) throws Exception {
        String url = ssoSetting.getUrls().BProductList();
        String post = "url" + catId +"url";
        BScrape scraper = new BScrape ();
        Map<String, String> postRequest = new HashMap();
        postRequest.put("searchRequestParams", post);
        return scrapeS.scrapePost(url, raw_cookie, postRequest, scraper);
    }
    

    I am also using resources from TutorialsPoint.

    It is happening within the @test method and I believe this mocking inside mocking is because I am using it wrong I am assuming.

    After Implementation from Answer (working):

    @Mock
    private SSetting sSetting = new SSetting ();
    // Mock:  uses Java Reflection in order to create mock objects and is injected into InjectMocks
    @Mock
    private ProdItem productItem = new ProdItem ();
    @Mock
    private sService scrapeService = new sService ();
    @Mock
    private BScrape bScrape ;
    
    //@InjectMocks annotation is used to create and inject the mock object
    @InjectMocks
    private BService bService = new BService ();
    
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }
    
    // Testing:
    @Test
    public void getProductList_Success() throws Exception{
    
        List<ProductItem> retList = new ArrayList<>();
        retList.add(new ProductItem());
        retList.add(new ProductItem());
    
        //try{
        when(b2SServices.getProductList("cookie", "6753")).thenCallRealMethod();
    
        //test the add functionality
        when(BService .getProdList("cookie", "6753")).thenReturn(retList);
    
    
        }catch(Exception exception){
            System.out.println(exception);
        }
    }