Spying with Mockito - to call or not to call a method


Posted by Steven

The other day a weird feature of the testing library Mockito tricked me. Allthough the behavior is mentioned in the documentation, it can not be bad to point it out here. Maybe this article spares someones time debugging his code.

I wanted to write a unit test for the class Controller. Controller uses a method of Blo, a Business Logic Object, to perform its tasks:

  1. public class Controller {
  2.  
  3. Blo blo;
  4.  
  5. public Controller(Blo blo) {
  6. this.blo = blo;
  7. }
  8.  
  9. public int run() {
  10.  
  11. // do important stuff here
  12.  
  13. return blo.doSomeStuff();
  14. }
  15. }
  1. public class Blo {
  2.  
  3. public int doSomeStuff() {
  4. System.out.println("doSomeStuff() was called!");
  5. return 42;
  6. }
  7.  
  8. // other important methods
  9. }

To unit-test run() in Controller, I have to decouple it from the real Blo. Otherwise the test would be an integration test instead of a unit test because Blo is also tested. Further, I want Blo to behave normally except for the one method I need for my test. That so called partial mocking can be done using Mockitos spy- method:

  1. public class ControllerTest {
  2.  
  3. @Test
  4. public void testStart() throws Exception {
  5. final Blo bloMock = spy(new Blo());
  6.  
  7. // mocking methods of the spy will be done here in just a moment ...
  8.  
  9. Controller s = new Controller(bloMock);
  10.  
  11. assertEquals(1, s.run());
  12. }
  13. }

So here is where I tricked myself. There are two ways to mock the method doSomeStuff() to return a 1 instead of the only true answer 42:

  1. when(bloMock.doSomeStuff()).thenReturn(1);

and

  1. doReturn(1).when(bloMock).doSomeStuff();

The very important difference is that the first option will actually call the doSomeStuff()- method while the second will not. Both will cause doSomeStuff() to return the desired 1.

So, why is this important? Imagine you have some side effects in doSomeStuff() that should not be called when your test is executed. That could be the reason why you mocked it in the first place. In my example code, that side effect is just printing a message, but it could be an important state change, the change of another class or whatever.

I hope some people can save time tracking that behavior by reading this article and remembering the difference between the two options.

Category: 
Share: 

Comments

the spy feature is not working basically lets say i spy on an object and i want to stub a method of that object but spy is invoking the real method itself. For example

Object obj = new Object();

Object spy = spy(obj);

doReturn(true).when(spy).method();

when you do this ..it is actually invoking and calling the real method.

any idea why 


Hello Thai,

I just wrote the following two classes using JUnit 4.11 and Mockito 1.9.5:

  1. public class Subject {
  2. public int doStuff() {
  3. System.out.println("doStuff called!");
  4. return 42;
  5. }
  6. }
  1. import org.junit.Test;
  2. import org.mockito.Mockito;
  3.  
  4. public class SubjectTest {
  5. @Test
  6. public void mockTest() {
  7. final Subject mock = Mockito.spy(new Subject());
  8.  
  9. // will be called:
  10. Mockito.when(mock.doStuff()).thenReturn(1);
  11.  
  12. // will not be called:
  13. Mockito.doReturn(1).when(mock).doStuff();
  14. }
  15. }

This proves my point of the article: the second call will not cause System.out to print something.

Can you confirm this with the given code?

So long,

Steven


Thanks Steven , it is userful answer ! :D


Can't we mock object Blo object int the constructor from unit testing,by creating the class by passingt a Mcoked Blo object?


That is what I've done in line 5 in the second code block. Is that what you mean?


spy() and mock() are two different things. The OP asked if you could mock() instead of spy(), and the answer is YES: you could do that to solve the same problem potentially. spy() is used when you want the real code of the class you are spying on to do its job, but be able to intercept method calls and return values. mock() is used to make a new class that has the same interface as the class you are mocking, but with NO code inside... so even if blo.doSomething() was called on the mocked class, it would do nothing no matter how you set it up.

 


Thanks Steven, this article helped!


Thank you for sharing this, I spent hours trying to get partial mocking to work without this first call to real method (causing NPE in my case...). 


Thanks a lot, you saved me from going crazy.
If i write the following:
PowerMockito.mockStatic(Util.class);
PowerMockito.doReturn("user").when(Util.class,"getUserName");

the result is that the actual "Util.getUserName()" method is called. The same behavior if i use "Powermockito.when.thenReturn" way. The same behavior if i use "Powermockito.stub(method()).thenReturn".

When i finally used "when" with only one parameter (the mocked class name, Util.class) IT WORKS!
PowerMockito.mockStatic(Util.class);
PowerMockito.doReturn("user").when(Util.class);

Great this article helped you! And thanks a lot for your comment :) Have fun!


It seems that the above is true for non-private methods.

Private methods are being invoked in both cases, at least in my experience. 


So how do I prevent private method from being called??


Hi Magie! Could you explain your question in more detail?


Hi how can in mock a private static method in a class.

and also wats the difference between

A a = PowerMockito.spy(new A())

and

PowerMockito.spy(A.class)


Hi Sreenath,

I didn't use PowerMockito, sorry. I know it's capable to mock private static methods, but I try to prevent doing this because it is a pretty big hack. ;)

Have fun,

Steven


I lost an hour but then I saved many more with this! Thanks!!!


I was running into the same issue, and your entry has saved me from getting crazy on it.

Besten Dank, und Beste Grüße aus nähe dein Bild :)


Great it saved you time, danke für das nette Kommentar! :)


Thank you! It solved my problem. And now I realize that many of my methods are called while testing, while I was sure that they are purely mocked.


Thank you. Very useful information.


literally,you save my day,the issue has drived me crazy!!


Thanks for nice tips... How to mock something like this : - 

Class A {

public void foo() {

Cookie cookie = new BasicCookie();

....

...

....

// Some basic logic

for(Cookie currentCookie : cookie.getListCookies() {

 // Some basic logic

}

}

}

 


Have you tried Powermock (https://github.com/powermock/powermock) ?