Spies

In Java, a spy is an object that is partially mocked. Unlike a regular mock, which creates a completely new instance of a class with all methods mocked, a spy maintains the original behavior of the object while allowing specific methods to be mocked.

Let's illustrate this with an example. Consider a simple Calculator class:

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public int subtract(int a, int b) {
        return a - b;
    }
}

Now, let's say we want to create a spy for this Calculator class and mock the add method while keeping the subtract method intact. We can achieve this using Mockito's spy() method.

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;

public class CalculatorTest {

    @Test
    public void testSpy() {
        // Create a spy for the Calculator class
        Calculator calculatorSpy = spy(new Calculator());
        
        // Mock the add method of the spy
        when(calculatorSpy.add(2, 3)).thenReturn(10); // Mocking add method
        
        // Call the subtract method (real implementation)
        int subtractResult = calculatorSpy.subtract(5, 3);
        
        // Call the add method (mocked behavior)
        int addResult = calculatorSpy.add(2, 3);
        
        // Verify that the subtract method works as expected (real implementation)
        assert(subtractResult == 2); // 5 - 3 = 2
        
        // Verify that the add method is mocked
        assert(addResult == 10); // Mocked result
    }
}

In this example:

  • We create a spy for the Calculator class using spy(new Calculator()).
  • We mock the behavior of the add method of the spy using when(calculatorSpy.add(2, 3)).thenReturn(10).
  • We call the subtract method of the spy, which invokes the real implementation of the subtract method.
  • We call the add method of the spy, which invokes the mocked behavior we defined earlier.
  • Finally, we verify that both the real implementation and the mocked behavior work as expected.

Using a spy allows you to selectively mock methods of an object while still using its real implementation for other methods.