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;
}
}
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:
mainModel.getList()
is called,when
is called,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:
mainModel.getList()
is called,when
is called,mock
of SomeModel
is created (inside getSomeList()
),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