Mockito @Spy Annotation Tutorial

Introduction

The @Spy annotation in Mockito is used to create a spy instance of a real object. Spies allow you to perform partial mocking, where some methods of the object are mocked while others retain their original behavior. This is particularly useful when you want to test a class with some real method calls and some mocked method calls. In this tutorial, we will demonstrate step by step how to use the @Spy annotation in Mockito.

Maven Dependencies

To use Mockito with JUnit 5, add the following dependencies to your pom.xml file:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>

Example Scenario

We will create a Calculator class with methods for addition and subtraction. Our goal is to test the Calculator methods using Mockito's @Spy annotation, where we will partially mock the subtraction method while keeping the addition method real.

Calculator Class

First, create the 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;
    }
}

JUnit 5 Test Class with Mockito

Create a test class for Calculator using JUnit 5 and Mockito.

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
public class CalculatorTest {

    @Spy
    private Calculator calculator;

    @Test
    public void testAdd() {
        // Given
        // No need to mock the add method, we want to use the real method

        // When
        int result = calculator.add(10, 20);

        // Then
        assertEquals(30, result);
    }

    @Test
    public void testSubtract() {
        // Given
        doReturn(5).when(calculator).subtract(10, 5);

        // When
        int result = calculator.subtract(10, 5);

        // Then
        assertEquals(5, result);
        verify(calculator).subtract(10, 5);
    }

    @Test
    public void testSubtractRealMethod() {
        // Given
        // No stubbing for subtract, so the real method will be called

        // When
        int result = calculator.subtract(10, 5);

        // Then
        assertEquals(5, result);
    }
}

Explanation

  1. Annotations:

    • @ExtendWith(MockitoExtension.class): Integrates Mockito with JUnit 5, enabling the use of Mockito annotations.
    • @Spy: Creates a spy instance of the Calculator class.
  2. testAdd():

    • Given: No need to mock the add method because we want to use the real method.
    • When: Calls the add method on the Calculator instance.
    • Then: Asserts that the result is 30.
  3. testSubtract():

    • Given: Uses doReturn(5).when(calculator).subtract(10, 5); to mock the subtract method.
    • When: Calls the subtract method on the Calculator instance.
    • Then: Asserts that the result is 5 and verifies that the subtract method was called with the arguments 10 and 5.
  4. testSubtractRealMethod():

    • Given: No stubbing for the subtract method, so the real method will be called.
    • When: Calls the subtract method on the Calculator instance.
    • Then: Asserts that the result is 5, confirming that the real method was executed.

Additional Scenario

Scenario: Combining Real and Mocked Methods

In this scenario, we will demonstrate how to combine real and mocked methods in a single test.

@Test
public void testAddAndSubtract() {
    // Given
    doReturn(3).when(calculator).subtract(7, 4);

    // When
    int additionResult = calculator.add(2, 3);
    int subtractionResult = calculator.subtract(7, 4);

    // Then
    assertEquals(5, additionResult); // Real method
    assertEquals(3, subtractionResult); // Mocked method
}

Explanation

  1. Given: Uses doReturn(3).when(calculator).subtract(7, 4); to mock the subtract method.
  2. When: Calls both the add and subtract methods on the Calculator instance.
  3. Then: Asserts that the result of the addition (real method) is 5 and the result of the subtraction (mocked method) is 3.

Conclusion

The @Spy annotation in Mockito allows you to create spies, enabling partial mocking of real objects. By using @Spy, you can keep the real behavior of some methods while mocking others, providing flexibility in your unit tests. This step-by-step guide demonstrated how to effectively use the @Spy annotation in your unit tests, covering different scenarios to ensure comprehensive testing of the Calculator class.

Related Mockito Annotations

Comments