Mockito @Captor Annotation

Introduction

In this chapter, we will learn about the @Captor annotation in Mockito. This annotation is used to create an ArgumentCaptor, which is a powerful feature for capturing arguments passed to mock methods. Captors allow you to verify and inspect the arguments that were passed to the methods of your mocks.

Key Points about the @Captor Annotation

  • Captures Arguments: The @Captor annotation creates an ArgumentCaptor to capture arguments passed to mock methods.
  • Verification and Inspection: Allows verification and inspection of method arguments to ensure they match expected values.
  • Simplifies Argument Capture: Reduces boilerplate code needed for capturing and asserting arguments.
  • Initialization: Requires initialization using MockitoAnnotations.openMocks(this).

Setting Up Mockito for @Captor 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 @Captor Annotation

Description

In this example, we will create an OrderService class that depends on a PaymentService class. The OrderService class will use the PaymentService class to process payments for orders. We will create unit tests for the OrderService class using Mockito’s @Captor annotation to capture and verify the arguments passed to the PaymentService.

Class Under Test: OrderService

public class OrderService {
    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void placeOrder(String productId, int quantity) {
        paymentService.processPayment(productId, quantity);
    }
}

Supporting Class: PaymentService

public class PaymentService {
    public void processPayment(String productId, int quantity) {
        // Logic to process payment
    }
}

Test Class: OrderServiceTest

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;

public class OrderServiceTest {

    @Mock
    private PaymentService paymentService;

    @InjectMocks
    private OrderService orderService;

    @Captor
    private ArgumentCaptor<String> productIdCaptor;

    @Captor
    private ArgumentCaptor<Integer> quantityCaptor;

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

    @Test
    void testPlaceOrder_CapturesArguments() {
        // Act
        orderService.placeOrder("product123", 2);

        // Assert
        verify(paymentService).processPayment(productIdCaptor.capture(), quantityCaptor.capture());
        assertEquals("product123", productIdCaptor.getValue());
        assertEquals(2, quantityCaptor.getValue());
    }
}

Explanation

  1. Mock Annotation:
    @Mock
    private PaymentService paymentService;
    

    The @Mock annotation creates a mock object for the PaymentService class.

  2. InjectMocks Annotation:
    @InjectMocks
    private OrderService orderService;
    

    The @InjectMocks annotation creates an instance of the OrderService class and injects the PaymentService mock into it.

  3. Captor Annotation:
    @Captor
    private ArgumentCaptor<String> productIdCaptor;
    
    @Captor
    private ArgumentCaptor<Integer> quantityCaptor;
    

    The @Captor annotation creates ArgumentCaptors for capturing the arguments passed to the processPayment method of the PaymentService mock.

  4. Initializing Mocks:
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    

    The MockitoAnnotations.openMocks(this) method initializes the mock objects and ArgumentCaptors. This is typically done in a @BeforeEach method to ensure mocks and captors are set up before each test.

  5. Test Method:
    @Test
    void testPlaceOrder_CapturesArguments() {
        // Act
        orderService.placeOrder("product123", 2);
    
        // Assert
        verify(paymentService).processPayment(productIdCaptor.capture(), quantityCaptor.capture());
        assertEquals("product123", productIdCaptor.getValue());
        assertEquals(2, quantityCaptor.getValue());
    }
    

    This test method captures the arguments passed to the processPayment method (Act), verifies that the method was called, and asserts that the captured arguments match the expected values (Assert).

Conclusion

The @Captor annotation in Mockito is used for capturing and verifying arguments passed to mock methods. It simplifies the process of capturing arguments, reducing boilerplate code and making tests cleaner and more maintainable. This approach helps ensure that the arguments passed to methods in your mocks are correct, improving the accuracy and reliability of your tests.

Leave a Comment

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

Scroll to Top