Mockito @Spy Annotation

Introduction

In this chapter, we will learn about the @Spy annotation in Mockito. This annotation is used to create spy objects, which are partial mocks of real objects. Spies allow you to call real methods while still being able to stub and verify certain behaviors.

Key Points about the @Spy Annotation

  • Partial Mocks: The @Spy annotation creates partial mocks that allow real methods to be called.
  • Real Method Invocation: Unlike mocks, spies call the actual methods unless they are stubbed.
  • Used for Real Objects: Useful when you want to test the real behavior of an object but need to stub specific methods.
  • Initialization: Requires initialization using MockitoAnnotations.openMocks(this).

Setting Up Mockito for @Spy Annotation

Ensure that you have the necessary dependencies in your pom.xml if you are using Maven:

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>5.2.0</version>
        <scope>test</scope>
    </dependency>
    <!-- JUnit 5 dependency -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Example: Using @Spy Annotation

Description

In this example, we will create a Calculator class with basic arithmetic operations. We will use the @Spy annotation to create a spy of the Calculator class, allowing us to call real methods while stubbing some behaviors.

Class Under Test: Calculator

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

    public int subtract(int a, int b) {
        return a - b;
    }

    public int multiply(int a, int b) {
        return a * b;
    }

    public int divide(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Division by zero");
        }
        return a / b;
    }
}

Test Class: CalculatorTest

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Spy;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;

public class CalculatorTest {

    @Spy
    private Calculator calculator;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testAdd_RealMethod() {
        // Act
        int result = calculator.add(10, 20);

        // Assert
        assertEquals(30, result);
        verify(calculator).add(10, 20);
    }

    @Test
    void testSubtract_RealMethod() {
        // Act
        int result = calculator.subtract(20, 10);

        // Assert
        assertEquals(10, result);
        verify(calculator).subtract(20, 10);
    }

    @Test
    void testMultiply_StubbedMethod() {
        // Arrange
        doReturn(100).when(calculator).multiply(10, 10);

        // Act
        int result = calculator.multiply(10, 10);

        // Assert
        assertEquals(100, result);
        verify(calculator).multiply(10, 10);
    }

    @Test
    void testDivide_RealMethod() {
        // Act
        int result = calculator.divide(20, 4);

        // Assert
        assertEquals(5, result);
        verify(calculator).divide(20, 4);
    }

    @Test
    void testDivideByZero_RealMethod() {
        // Act & Assert
        assertThrows(IllegalArgumentException.class, () -> calculator.divide(10, 0));
        verify(calculator).divide(10, 0);
    }
}

Explanation

  1. Spy Annotation:
    @Spy
    private Calculator calculator;
    

    The @Spy annotation creates a spy object for the Calculator class. This allows real methods to be called while also enabling stubbing and verification.

  2. Initializing Spies:
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    

    The MockitoAnnotations.openMocks(this) method initializes the spy objects. This is typically done in a @BeforeEach method to ensure spies are set up before each test.

  3. Test Method:
    @Test
    void testMultiply_StubbedMethod() {
        // Arrange
        doReturn(100).when(calculator).multiply(10, 10);
    
        // Act
        int result = calculator.multiply(10, 10);
    
        // Assert
        assertEquals(100, result);
        verify(calculator).multiply(10, 10);
    }
    

    This test method demonstrates stubbing a method in the spy. The real multiply method is overridden to return 100, and the result is verified.

Output

Mockito @Spy Annotation

Conclusion

The @Spy annotation in Mockito is used for creating partial mocks. It allows you to call real methods while also enabling stubbing and verification of specific behaviors. In this section, we demonstrated how to use the @Spy annotation with a simple Calculator example. This approach helps you test the real behavior of objects while still having control over certain methods, making your tests more comprehensive and maintainable.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top