Mockito Trying to spy on method is calling the original method
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Mockito is a popular mocking framework for unit tests in Java. One of its powerful features is the ability to create spies, or partial mocks, of real objects. However, a common issue that developers encounter when using Mockito spies is that trying to stub a method can result in the original method being called. This can lead to unexpected behavior and can compromise the integrity of a unit test. Understanding why this happens and how to handle it is crucial for effectively using spies in testing scenarios.
Understanding Mockito Spies
A spy in Mockito is an actual instance of the class being mocked, with some of its methods stubbed or overridden. Unlike mocks that require explicit behavior definition for all their methods, spies by default call the real methods of the object. This is useful when you want to mock some methods of a class but leave the rest unchanged.
Here's an example of creating a spy:
In this example, spyList is a spy of list. By default, all methods of spyList call the corresponding methods of list unless explicitly stubbed.
The Problem with Method Calls in Spies
When you try to stub a method on a spy, you might unintentionally trigger the original method's execution. This typically occurs due to the way Mockito handles method interceptions in spies. Here is an example that illustrates this problem:
Here, instead of setting up the stub, the add method is actually called on the real calc object, resulting in the actual execution of its logic. This happens because the method call inside when() is performed on the spy before Mockito has a chance to intercept and stub it.
Correct Way to Stub Methods in Spies
To avoid this pitfall, Mockito provides the doReturn() method, which should be used for stubbing spies. Here’s how you can correctly stub the add method on a spy:
Using doReturn() avoids calling the original method and safely sets up your stub.
Technical Reasons Behind this Behavior
The technical reason behind this behavior is related to how Mockito's invocation handler processes method calls on spies. When when() is used, the method gets executed before Mockito can replace it with the stubbed value. Hence, any side effects inside the method (like changing a field's value) are inadvertently triggered. doReturn(), on the other hand, skips the method invocation and directly attaches the stubbed value to the specified method call in the spy.
Summary Table
| Method | Use Case | Behavior with Spies |
when().thenReturn() | Stubs a method's return value | Can invoke the actual method's logic |
doReturn().when() | Stubs a method's return value without invoking the actual method | Safe for spies, does not invoke the actual method's logic |
Conclusion
Using spies effectively in Mockito requires an understanding of the nuances of method stubbing. Incorrect stubbing can lead not only to faulty tests but also to misleading results and increased debugging time. Remember to use doReturn() when you need to stub a method in a spy to ensure that your tests remain sound and predictable.
In summary, Mockito is an extremely useful tool in a developer's testing arsenal, with capabilities that extend beyond simple mocks to sophisticated spying mechanisms. Properly leveraging these capabilities ensures thorough and accurate testing practices.

