Capturing Arguments with Mockito ArgumentCaptor: Examples and Use Cases

Discover how to capture and verify method arguments in Java unit tests using Mockito’s ArgumentCaptor with real-world scenarios.

Get Started free
Guide Banner Image
Home Guide Capturing Arguments with Mockito ArgumentCaptor: Examples and Use Cases

Capturing Arguments with Mockito ArgumentCaptor: Examples and Use Cases

Unit testing is an essential part of the software development cycle as it ensures that the code behaves as expected. Mocking dependencies is crucial when creating unit tests to separate the units being tested. Mockito is a popular mocking framework in Java that enables developers to create and manage mock objects effectively.

Overview

Why use Mockito ArgumentCaptor?

  • Dynamic Argument Verification
  • Improved Test Debugging
  • Handling Callbacks and Listeners
  • Capturing Complex Objects
  • Multiple Invocation Handling
  • Validating API Requests

Use Cases of ArgumentCaptor in Unit Testing

  • Ensure correct arguments are passed to mocked methods.
  • Track arguments across multiple method invocations.
  • Validate complex object parameters.
  • Verify lists or collections passed to methods.
  • Confirm argument accuracy during exception flows.

Types of Mockito ArgumentCaptor usage:

  • Single Argument Capturing: Capture and verify a single argument passed to a method.
  • Multiple Arguments Capturing: Capture multiple arguments from the same or multiple method calls.
  • Multiple Captors for Different Arguments: Use separate captors to capture and verify different parameters in a single method call.
  • Capturing Arguments from Multiple Invocations: Retrieve argument values passed across repeated method calls.

This article explains the concept of Mockito ArgumentCaptor in detail, with examples.

What is Mockito?

Mockito is a popular Java mocking framework used in unit testing. It lets the developers create mock objects, stub method behaviors, and verify interactions without relying on real dependencies.

One key aspect of Mockito is verifying interactions with mock objects, particularly capturing the method arguments. This is where ArgumentCaptor plays its role. It allows developers to capture arguments passed to mock methods, analyze them, and verify that expected values are being used.

What is Mockito ArgumentCaptor?

Mockito ArgumentCaptor is a utility that captures method arguments passed to mock objects during unit testing. It allows us to verify the actual values used in method calls, which is helpful when dealing with dynamically generated parameters, event listeners, or complex objects in test cases.

Why use Mockito ArgumentCaptor?

Following are the reasons to use ArgumentCaptor:

  • Dynamic Argument Verification: To confirm that the actual method arguments match the expectations.
  • Improved Test Debugging: It helps to inspect arguments passed to methods.
  • Handling Callbacks and Listeners: Helpful when dealing with event-driven interactions.
  • Capturing Complex Objects: To verify object properties dynamically.
  • Multiple Invocation Handling: Capture and verify arguments from multiple calls.
  • Validating API Requests: To ensure correct API calls are being made.

When to use Mockito ArgumentCaptor?

Mockito ArgumentCaptor is very useful in the below scenarios.

  • When verifying methods that accept dynamically generated arguments.
  • While debugging test failures related to incorrect argument passing.
  • During testing of event-driven systems where listeners receive dynamic values.
  • When dealing with APIs that require detailed assertions on method inputs.
  • While testing notification or messaging services that rely on variable parameters.

Setting Up Mockito and ArgumentCaptor

The requirements below are mandatory for the initial setup of Mockito and ArgumentCaptor.

Required Dependencies (Maven/Gradle)

When using Eclipse with inbuilt Maven, the following dependencies should be added to your pom.xml file.

<dependencies>

<!-- JUnit 5 -->

<dependency>

<groupId>org.junit.jupiter</groupId>

<artifactId>junit-jupiter-api</artifactId>

<version>5.9.0</version>

<scope>test</scope>

</dependency>

<!-- Mockito Core -->

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-core</artifactId>

<version>5.0.0</version>

<scope>test</scope>

</dependency>

<!-- Mockito JUnit Integration -->

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-junit-jupiter</artifactId>

<version>5.0.0</version>

<scope>test</scope>

</dependency>

</dependencies>
Copied

Importing Mockito and ArgumentCaptor

After setting up the dependencies, the required classes are to be imported to java test file:

import org.junit.jupiter.api.Test;

import org.mockito.ArgumentCaptor;

import static org.mockito.Mockito.*;

import static org.junit.jupiter.api.Assertions.*;
Copied

Example of Capturing a Single Argument

Below is an example of Mockito ArgumentCaptor, where a class CartService interacts with interface CartRepository to add products to cart. Instead of calling the real API, the repository is mocked and arguments are captured to verify their correctness.

Step 1: To create CartRepository Interface

public interface CartRepository {

    void addToCart(String productId, int quantity);

}
Copied

Step 2: To create CartService Class

public class CartService {

private final CartRepository cartRepository;

    public CartService(CartRepository cartRepository) {

        this.cartRepository = cartRepository;

    }

    public void addProductToCart(String productId, int quantity) {

        cartRepository.addToCart(productId, quantity); // Used to call repository method

    }

}
Copied

Step 3: To write unit test using Mockito ArgumentCaptor

import org.junit.jupiter.api.Test;

import org.mockito.ArgumentCaptor;

import static org.mockito.Mockito.*;

import static org.junit.jupiter.api.Assertions.*;




class CartServiceTest {

    @Test

    void testAddProductToCart() {




    // Step 1: Mock the CartRepository (This will prevent actual API calls to bstackdemo.com)

        CartRepository mockRepository = mock(CartRepository.class);

        CartService cartService = new CartService(mockRepository);




        // Step 2: Call method under test (Mimics a user adding an iPhone 13 to the cart with quantity 2)

        cartService.addProductToCart("iphone13", 2);




        // Step 3: Create ArgumentCaptors for productId and quantity (Capture Arguments Passed to addToCart())

        ArgumentCaptor<String> productIdCaptor = ArgumentCaptor.forClass(String.class);

        ArgumentCaptor<Integer> quantityCaptor = ArgumentCaptor.forClass(Integer.class);




       // Step 4: Verify and capture the actual arguments (Verify addToCart() Was Called and Capture Its Arguments)

        verify(mockRepository).addToCart(productIdCaptor.capture(), quantityCaptor.capture());




        // Step 5: Assert captured values (Validate Captured Values)

        assertEquals("iphone13", productIdCaptor.getValue());

        assertEquals(2, quantityCaptor.getValue());

    }

}
Copied

Verifying Captured Arguments

The getValue() method captures the most recent value, ensuring correctness. Once the arguments are captured using ArgumentCaptor, it has to be verified properly.

(Note: If the method is called with “iphone14” instead of “iphone13”, the assertion would fail).

Capturing Multiple Arguments

The following is an example of how to capture multiple arguments using ArgumentCaptor.

 Using capture() Multiple Times

import org.junit.jupiter.api.Test;

import org.mockito.ArgumentCaptor;

import static org.mockito.Mockito.*;

import static org.junit.jupiter.api.Assertions.*;

import java.util.List;




class MultipleCartServiceTest {

    @Test

    void testAddMultipleProductsToCart() {

        // To mock the repository

        CartRepository mockRepository = mock(CartRepository.class);

        CartService cartService = new CartService(mockRepository);




        cartService.addProductToCart("iphone13", 2);

        cartService.addProductToCart("macbookpro", 1);




        // To capture multiple arguments

        ArgumentCaptor<String> productIdCaptor = ArgumentCaptor.forClass(String.class);

        ArgumentCaptor<Integer> quantityCaptor = ArgumentCaptor.forClass(Integer.class);




        verify(mockRepository, times(2)).addToCart(productIdCaptor.capture(), quantityCaptor.capture());




        // To get the captured values

        List<String> capturedProductIds = productIdCaptor.getAllValues();

        List<Integer> capturedQuantities = quantityCaptor.getAllValues();




        // Verify the captured values

        assertEquals("iphone13", capturedProductIds.get(0));

        assertEquals(2, capturedQuantities.get(0));




        assertEquals("macbookpro", capturedProductIds.get(1));

        assertEquals(1, capturedQuantities.get(1));

    }

}
Copied

Note:

  • times(2) makes sure that addToCart() is called twice
  • getAllValues() will get all captured arguments in order.
  • Code verifies product IDs (iphone13, macbookpro) and its quantities (2, 1).

Handling Lists and Collections

Step 1: Create an Interface (CartRepository)

import java.util.List;

public interface CartRepository {

    void addToCart(String productId, int quantity);

    void addProducts(List<String> productIds);

}
Copied

Step 2: Create a Class (CartService)

import java.util.List;

public class CartService {

    private final CartRepository cartRepository;




    public CartService(CartRepository cartRepository) {

        this.cartRepository = cartRepository;

    }




    public void addProductToCart(String productId, int quantity) {

        cartRepository.addToCart(productId, quantity);

    }

    public void addProductsToCart(List<String> productIds) {

        cartRepository.addProducts(productIds);

    }

}
Copied

Step 3: Create a unit test class

import org.junit.jupiter.api.Test;

import org.mockito.ArgumentCaptor;

import static org.mockito.Mockito.*;

import static org.junit.jupiter.api.Assertions.*;

import java.util.Arrays;

import java.util.List;




class MultipleCartServiceTest {

@Test

void testAddMultipleProductsAsList() {

    // To mock the repository

    CartRepository mockRepository = mock(CartRepository.class);

    CartService cartService = new CartService(mockRepository);




    // To create product list

    List<String> productList = Arrays.asList("iphone13", "macbookpro", "ipad");

    cartService.addProductsToCart(productList);




    ArgumentCaptor<List<String>> productListCaptor = ArgumentCaptor.forClass(List.class);

    verify(mockRepository).addProducts(productListCaptor.capture());

    

    //Verify the captured arguments

    assertEquals(productList, productListCaptor.getValue());

}

}
Copied

Talk to an Expert

ArgumentCaptor (Manual Instantiation) vs. @Captor Annotation

ArgumentCaptor can either be instantiated manually or using @Captor annotation.

  • For manual instantiation, an ArgumentCaptor is explicitly created using forClass().
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
Copied
  • Instead of manual, @Captor annotation also can be used to define captors as instance variables.
@Captor**

ArgumentCaptor<String> captor;
Copied

**@Captor simplifies initialization, making tests more readable.

Use Cases of ArgumentCaptor in Unit Testing

Listed below are the key uses of ArgumentCaptor.

  1. Verify Method Calls: To ensure that correct arguments are passed to the mocked method.
  2. Capture Multiple Calls: To track arguments when a method is being called multiple times.
  3. Capture Objects Instead of Primitives: To validate complex objects passed as parameters.
  4. Capture Lists and Collections: To verify if a list or collection of values is passed right.
  5. Test Exception Scenarios: To ensure correct arguments are passed even when exceptions occur.

BrowserStack Automate Banner

Best Practices for using ArgumentCaptor

The following are to be kept in mind when using ArgumentCaptor.

  1. Use @Captor Annotation for neat and more readable code.
  2. Make Sure to Verify Before Capturing using verify(mock).method(argumentCaptor.capture());.
  3. Apply getAllValues() method to capture multiple calls instead of just the last value.
  4. Write Proper Assertions to check if captured arguments match expected values.

Why test Mocked Exceptions on Real Devices?

While mocking exceptions in unit tests helps simulate failure scenarios, real devices provide a more accurate and reliable execution environment, ensuring that your application’s error handling works as expected in real-world conditions.

BrowserStack Automate enables you to run tests on real browsers and devices in the cloud, going beyond mock scenarios to validate behavior across diverse environments. When combined with mocked exception testing, it ensures a comprehensive and scalable testing strategy for any Java application.

Conclusion

Mockito ArgumentCaptor is an efficient tool that enhances Java unit testing by capturing and verifying method arguments dynamically. It helps simplify debugging, improve test accuracy, and ensure the expected behaviors. Teams trying to optimize their testing processes and make sure their apps run smoothly on all devices and browsers will find BrowserStack Automate to be the perfect answer.

Try BrowserStack Now

FAQs

1. What is Mockito ArgumentCaptor used for?

It captures method arguments passed to mock objects for verification in unit tests.

2. Can ArgumentCaptor capture multiple values?

Yes, getAllValues() retrieves all captured arguments from multiple method calls.

3. Is ArgumentCaptor better than Mockito matchers?

It depends on the use case. Matchers work well for simple cases, but ArgumentCaptor provides better insight into actual arguments.

Tags
Automation Testing Website Testing