Mockito @Spy Annotation Tutorial

1. Overview

Mockito is a robust testing framework for Java applications, and it offers several annotations to make the mock creation process straightforward. The @Spy annotation is one of those. While the @Mock annotation creates a complete mock, the @Spy annotation creates a "partial mock." With a partial mock, you can mock only specific methods of a real object, while other methods will retain their original behavior. In this tutorial, we'll dive deep into the @Spy annotation.

2. Development Steps

1. Set up a Maven project.

2. Integrate the necessary dependencies (JUnit 5 and Mockito).

3. Construct a service class with a couple of methods.

4. Frame a test class for the service.

5. Use the @Spy annotation to create a spy of the real object.

6. Define and execute test cases.

7. Review the test results.

3. Dependencies (Mockito and JUnit 5)

Step 1 and Step 2: Create a simple Maven project and add the following Mockito and JUnit 5 dependencies:
<!-- JUnit 5 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>5.6.0</version>
    <scope>test</scope>
</dependency>

4. Code Program

// Step 3: Design the Service class
class SampleService {
    String actualMethod() {
        return "Real implementation of actualMethod";
    }
    String toBeSpiedMethod() {
        return "Real implementation of toBeSpiedMethod";
    }
}
// Step 4 and 5: Compose a test class for SampleService
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;
@ExtendWith(MockitoExtension.class)
public class SampleServiceTest {
    @Spy
    private SampleService sampleServiceSpy;
    @Test
    public void toBeSpiedMethodTest() {
        // Modify behavior of one method using spy
        doReturn("Spied version of toBeSpiedMethod").when(sampleServiceSpy).toBeSpiedMethod();
        String result = sampleServiceSpy.toBeSpiedMethod();
        assertEquals("Spied version of toBeSpiedMethod", result);
    }
    @Test
    public void actualMethodTest() {
        String result = sampleServiceSpy.actualMethod();
        assertEquals("Real implementation of actualMethod", result);
    }
}

Output:

Both tests, toBeSpiedMethodTest and actualMethodTest, will pass successfully, demonstrating the dual behavior of the spied object.

Code Explanation:

1. After setting up Mockito and JUnit 5 dependencies, we crafted a class named SampleService with two methods.

2. In our test class, we utilized the @Spy annotation to create a spy of the real object SampleService.

3. With spies, we can selectively modify the behavior of specific methods. For instance, in the toBeSpiedMethodTest method, we altered the behavior of toBeSpiedMethod to return a spied version using doReturn...when construct.

4. However, in the actualMethodTest, we didn't modify any behavior. Therefore, the method retained its original implementation.

5. This illustrates the power of the @Spy annotation: we can choose to mock certain methods while allowing others to execute their real implementations.

5. Conclusion

The @Spy annotation in Mockito empowers developers to create partial mocks effortlessly, which can be incredibly beneficial in scenarios where only specific methods of a real object need to be mocked. When used judiciously, spies can be a valuable tool for writing comprehensive and flexible unit tests.

Related Mockito Annotations

Comments