2

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);
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
L1ghtk3ira
  • 3,021
  • 6
  • 31
  • 70

2 Answers2

4

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);
Community
  • 1
  • 1
Liviu Stirb
  • 5,876
  • 3
  • 35
  • 40
  • Sorry I do not fully understand? Can you go into a bit more detail with an example? Thanks so much – L1ghtk3ira Jun 10 '16 at 12:01
  • I think you and SpaceTrucker are close, I think i am doing it wrong. I added the method for which I am using for the test. Did not show Url so its a dummy url. – L1ghtk3ira Jun 10 '16 at 12:39
  • I appreciate the help by the way – L1ghtk3ira Jun 10 '16 at 13:48
  • @L1ghtk3ira I think your getProdList or getProductList (since the name is different) is a static method since you didn't call it on bService instance. You did not post the full code and this is hard to speculate what you are doing – Liviu Stirb Jun 10 '16 at 14:03
  • if this is the case http://stackoverflow.com/questions/4482315/why-does-mockito-not-mock-static-methods you will also need power mockito – Liviu Stirb Jun 10 '16 at 14:06
1

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);
SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
  • I think you and user1121883 are close, I think i am doing it wrong. I added the method for which I am using for the test. Did not show Url so its a dummy url. – L1ghtk3ira Jun 10 '16 at 12:38
  • I think I was using it wrong I only want to use the one method. So I think I only need this in yours : when(bService.getProductList("cookie", "6753")).thenReturn(returnValue); – L1ghtk3ira Jun 10 '16 at 12:41
  • Taking out my try catch it fails here: String url = ssoSetting.getUrls().BProductList(); – L1ghtk3ira Jun 10 '16 at 13:49
  • You seem to not have injected the mock of `BService ` to the instance under test. However if your original question has been answered consider accepting an answer. You may ask a new question, if you are not able to solve your new problem after you have done some research on your own. – SpaceTrucker Jun 10 '16 at 13:58
  • Very true I got off topic at the end. I marked user1121883 as it is more in dept and his last code solved the implementation. I am also giving you an up vote because I believe showing the steps as you did will help some people see the key order in case they are reviewing their code incorrectly. – L1ghtk3ira Jun 10 '16 at 14:07