Mockito
Spy
Unit Testing
Java
Software Development

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:

 
List<String> list = new ArrayList<>();
List<String> spyList = Mockito.spy(list);

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:

 
1class Calculator {
2    public int add(int a, int b) {
3        return a + b;
4    }
5}
6
7Calculator calc = new Calculator();
8Calculator spyCalc = Mockito.spy(calc);
9
10// Attempt to stub the add method
11Mockito.when(spyCalc.add(1, 2)).thenReturn(0);

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:

 
Mockito.doReturn(0).when(spyCalc).add(1, 2);

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

MethodUse CaseBehavior with Spies
when().thenReturn()Stubs a method's return valueCan invoke the actual method's logic
doReturn().when()Stubs a method's return value without invoking the actual methodSafe 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.


Course illustration
Course illustration

All Rights Reserved.