Unfinished Stubbing Detected in Mockito
Asked Answered
A

8

256

I am getting following exception while running the tests. I am using Mockito for mocking. The hints mentioned by Mockito library are not helping.

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
    -> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)

    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!

        at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
        ..........

Test Code from DomainTestFactory. When I run the following test, I see the exception.

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); // Line 355
}

private List<SomeModel> getSomeList() {
    SomeModel model = Mockito.mock(SomeModel.class);
    Mockito.when(model.getName()).thenReturn("SomeName"); // Line 276
    Mockito.when(model.getAddress()).thenReturn("Address");
    return Arrays.asList(model);
}

public class SomeModel extends SomeInputModel{
    protected String address;
    protected List<SomeClass> properties;

    public SomeModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    public String getAddress() {
        return this.address;
    }

}

public class SomeInputModel{

    public NetworkInputModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    protected String Name;
    protected List<SomeClass> properties;

    public String getName() {
        return this.Name;
    }

    public void setName(String value) {
        this.Name = value;
    }
}
Arnone answered 11/10, 2014 at 19:42 Comment(0)
U
547

You're nesting mocking inside of mocking. You're calling getSomeList(), which does some mocking, before you've finished the mocking for MyMainModel. Mockito doesn't like it when you do this.

Replace

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}

with

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    List<SomeModel> someModelList = getSomeList();
    Mockito.when(mainModel.getList()).thenReturn(someModelList);
}

To understand why this causes a problem, you need to know a little about how Mockito works, and also be aware in what order expressions and statements are evaluated in Java.

Mockito can't read your source code, so in order to figure out what you are asking it to do, it relies a lot on static state. When you call a method on a mock object, Mockito records the details of the call in an internal list of invocations. The when method reads the last of these invocations off the list and records this invocation in the OngoingStubbing object it returns.

The line

Mockito.when(mainModel.getList()).thenReturn(someModelList);

causes the following interactions with Mockito:

  • Mock method mainModel.getList() is called,
  • Static method when is called,
  • Method thenReturn is called on the OngoingStubbing object returned by the when method.

The thenReturn method can then instruct the mock it received via the OngoingStubbing method to handle any suitable call to the getList method to return someModelList.

In fact, as Mockito can't see your code, you can also write your mocking as follows:

mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);

This style is somewhat less clear to read, especially since in this case the null has to be casted, but it generates the same sequence of interactions with Mockito and will achieve the same result as the line above.

However, the line

Mockito.when(mainModel.getList()).thenReturn(getSomeList());

causes the following interactions with Mockito:

  1. Mock method mainModel.getList() is called,
  2. Static method when is called,
  3. A new mock of SomeModel is created (inside getSomeList()),
  4. Mock method model.getName() is called,

At this point Mockito gets confused. It thought you were mocking mainModel.getList(), but now you're telling it you want to mock the model.getName() method. To Mockito, it looks like you're doing the following:

when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);

This looks silly to Mockito as it can't be sure what you're doing with mainModel.getList().

Note that we did not get to the thenReturn method call, as the JVM needs to evaluate the parameters to this method before it can call the method. In this case, this means calling the getSomeList() method.

Generally it is a bad design decision to rely on static state, as Mockito does, because it can lead to cases where the Principle of Least Astonishment is violated. However, Mockito's design does make for clear and expressive mocking, even if it leads to astonishment sometimes.

Finally, recent versions of Mockito add an extra line to the error message above. This extra line indicates you may be in the same situation as this question:

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

Unconventional answered 11/10, 2014 at 21:19 Comment(9)
Is there any explanation of this fact? Solution works. And I don't understand why 'in-place' mock creation doesn't work. When you create mock and pass created mock by reference to other mock, it works.Bluey
Excellent answer, love SO ! It would have taken me ages to find this by myselfDraughtsman
Awesome. The funny thing is, when I do the direct method call and debug slowly, then it works. The Attribute of CGLIB$BOUND will get the value true, but somehow it takes a little time. When I use the direct method call and stop before training (when ...), then I see the value is first false, and becomes later true. When it is false and training starts, then this exception occures.Alatea
Great answer Luke! A lot of things around Mockito is clear now. Thank you very much.Edin
I'm getting this error in a more complicated scenario involving multi-threading, and I don't seem to be doing mocking-inside-mocking. Rather, I'm suspecting a thread safety issue inside Mockito. Anyone had this?Dobbins
I get this kind of problem while building my release in Jenkins, on my Local Machine it works fine. If I understand correct - its ok if I will use static methods as a parameter of thenReturn ?Helman
@BOTU: sorry, but it's impossible to comment any further without seeing any code.Unconventional
Cool @LukeWoodward But if we will think about the specifics of all tools every time, then there will be no time left to program. Mockito, to be honest, is a very inconvenient and confusing tool, but i have to use it at work. In general, it's strange to me why it's impossible to create an array inside thenReturn on the fly, so as not to suffer. What prevented the developers from making this feature is unclear.Indianapolis
@avgolubev: It's not impossible to create an array inside a method called by .thenReturn. You'll just run into problems if you attempt to any further mock setup in this method. Personally, I find it simpler to not have method calls in .thenReturn anyway. What you (and everyone reading this question) have run into is a leaky abstraction, and all abstractions are leaky. There will always be details and specifics of the tools we use that we need to know about.Unconventional
P
7

AbcService abcService = mock(AbcService.class);

Check the syntax:

  1. doThrow(new RunTimeException()).when(abcService).add(any(), any())

Common Mistake as seen below:

A. doThrow(new RunTimeException()).when(abcService.add(any(), any()))

Similarly, check for when().thenReturn(), so on.

Pursuer answered 3/9, 2021 at 16:40 Comment(0)
C
5

For those who use com.nhaarman.mockitokotlin2.mock {}

Workaround 1

This error occurs when, for example, we create a mock inside another mock

mock {
    on { x() } doReturn mock {
        on { y() } doReturn z()
    }
}

The solution to this is to create the child mock in a variable and use the variable in the scope of the parent mock to prevent the mock creation from being explicitly nested.

val liveDataMock = mock {
        on { y() } doReturn z()
}
mock {
    on { x() } doReturn liveDataMock
}

Workaround 2

Make sure all your mocks that should have a thenReturn.

GL

Cathode answered 4/2, 2021 at 17:38 Comment(2)
Also for com.nhaarman.mockitokotlin2.mock make sure all your mocks that should have a thenReturn have a thenReturn. For me it wasn't the mock that it was complaining about that was the issue.Ahrens
@NarutoSempai Perfect, answer updated. I invite you to describe more details in workaround 2 if you consider them necessary. ThanksCathode
A
3

I had the same error when I used by mistake:

doReturn(value).when(service.getValue());

instead of

doReturn(value).when(service).getValue();
Atmo answered 5/10, 2023 at 12:3 Comment(0)
C
2
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.

For mocking of void methods try out below:

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
Constellation answered 10/10, 2019 at 12:13 Comment(0)
R
0

I am so exited with detailed answer of @Luke Woodward that want to share a workaround. As @Luke Woodward explained, we can not have two calls like

when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);

Than can occurs in call chain. But in case you will use construction:

doReturn(mockToken("token3")).when(mock).getAccessToken();

when

OAuth2AccessToken mockToken(String tokenVal){
        OAuth2AccessToken token = Mockito.mock(OAuth2AccessToken.class);
        doReturn( 60 ).when(token).getExpiresIn();
        doReturn(tokenVal).when(token).getValue();
        return token;
    }

all will works as expected.

Regress answered 25/1, 2022 at 21:26 Comment(0)
K
0

I am using Mockito for Android in Kotlin and couldn't find an apt solution for my case.

I want a way to mock an exception. The code, has to be tested if the UI Status is aptly updating to ErrorState when the exception is thrown

`when`(mockApi.getData()).thenAnswer {
            throw NotConnectedToInternetException()
        }
viewModel.getData()
assertEquals(viewModel.apiResult.value!!.message, "Make sure you have an active network connection") 

when-thenAnswer can be replaced with given-willAnswer depending the library used

Hope this helps someone

Kampmeier answered 16/10, 2023 at 4:7 Comment(0)
P
0

In my case it turns out that there is an exception / error for the thenReturn part

when(serviceA.methodX(y)).thenReturn(TestUtil.createMockData(-1));

The TestUtil.createMockData(-1) failed to use reflection to create the return mock data, if we do it like this

doReturn(TestUtil.createMockData(-1)).when(serviceA).methodX(y);

The error is easier to spot

Plainclothesman answered 24/4, 2024 at 6:34 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.