Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito: using a method in "thenReturn" to return a mock doesn't work

I have encountered what I assume might be a bug with Mockito, but was wondering if anyone else can shed light as to why this test doesn't work.

Basically, I have two objects, like this:

public class FirstObject {     private SecondObject secondObject;     public SecondObject getSecondObject() { return secondObject; } }  public class SecondObject {     private String name;     public String getName() { return name; } } 

The first object is mocked via annotation and the before method:

@Mock FirstObject mockedFirstObject;  @Before public void setup() {     MockitoAnnotations.initMocks(this); } 

The second object is mocked in a method:

public SecondObject setupMockedSecondObject() {     SecondObject secondObject = Mockito.mock(SecondObject.class);     Mockito.when(secondObject.getName()).thenReturn("MockObject");     return secondObject; } 

When thenReturn contains a direct call to this method to setup and obtain a mock of the second object, it fails:

@Test public void notWorkingTest() {     Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());     Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject"); } 

But, when the mock returned by the same method is assigned to a local variable, which is used in thenReturn, it works:

@Test public void workingTest() {     SecondObject mockedSecondObject = setupMockedSecondObject();     Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);     Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject"); } 

Are we doing something wrong or is this indeed a bug/limitation in Mockito? Is there a deliberate reason for this not working?

like image 943
LawrenceWeetman Avatar asked Nov 30 '15 18:11

LawrenceWeetman


People also ask

What does thenReturn do in Mockito?

The thenReturn() methods lets you define the return value when a particular method of the mocked object is been called.

How do you call a real method in Mockito?

Mockito allows us to partially mock an object. This means that we can create a mock object and still be able to call a real method on it. To call a real method on a mocked object we use Mockito's thenCallRealMethod().

How do I return an object in Mockito?

In Mockito, you can specify what to return when a method is called. That makes unit testing easier because you don't have to change existing classes. Mockito supports two ways to do it: when-thenReturn and doReturn-when . In most cases, when-thenReturn is used and has better readability.

How do you mock method which returns void?

Mockito provides following methods that can be used to mock void methods. doAnswer() : We can use this to perform some operations when a mocked object method is called that is returning void. doThrow() : We can use doThrow() when we want to stub a void method that throws exception.


1 Answers

This is indeed a limitation of Mockito, and it is referenced in their FAQ:

Can I thenReturn() an inlined mock()?

Unfortunately you cannot do this:

when(m.foo()).thenReturn(mock(Foo.class)); //                         ^ 

The reason is that detecting unfinished stubbing wouldn't work if we allow above construct. We consider is as a 'trade off' of framework validation (see also previous FAQ entry). However you can slightly change the code to make it working:

//extract local variable and start smiling: Foo foo = mock(Foo.class); when(m.foo()).thenReturn(foo); 

The workaround, as mentioned, is to store the desired returned value in a local variable, like you have done.

The way I understand it is that Mockito validates the usage you make of it every time you call its methods. When another method is called during an on-going stubbing process, you are breaking its validation process.

like image 137
Tunaki Avatar answered Sep 23 '22 17:09

Tunaki