Mockito ArgumentMatchers

Introduction

Mockito provides ArgumentMatchers to specify flexible argument constraints while verifying or stubbing method calls on mock objects. This is particularly useful when the exact values of arguments are not crucial, or when you want to apply conditions to the arguments. This tutorial will demonstrate how to use various ArgumentMatchers in Mockito to handle flexible argument matching.

In Mockito, while writing test cases, there may arise situations where you don't care about the input argument of a method call; you just want the method to return a certain value or behave in a specific way regardless of what is passed to it. This is where ArgumentMatchers comes into play. They allow you to specify generic arguments that match certain criteria instead of specific values.

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 CustomerService class that has a dependency on a CustomerRepository. Our goal is to test the CustomerService methods using various ArgumentMatchers in Mockito to handle flexible argument matching.

CustomerService and CustomerRepository Classes

First, create the Customer class, the CustomerRepository interface, and the CustomerService class.

public class Customer {
    private String name;
    private String email;

    // Constructor, getters, and setters
    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

public interface CustomerRepository {
    void saveCustomer(Customer customer);
    Customer findCustomerByEmail(String email);
    List<Customer> findAllCustomers();
}

public class CustomerService {
    private final CustomerRepository customerRepository;

    public CustomerService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    public void registerCustomer(String name, String email) {
        Customer customer = new Customer(name, email);
        customerRepository.saveCustomer(customer);
    }

    public Customer getCustomerByEmail(String email) {
        return customerRepository.findCustomerByEmail(email);
    }

    public List<Customer> getAllCustomers() {
        return customerRepository.findAllCustomers();
    }
}

JUnit 5 Test Class with Mockito

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

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

import java.util.Arrays;
import java.util.List;

@ExtendWith(MockitoExtension.class)
public class CustomerServiceTest {

    @Mock
    private CustomerRepository customerRepository;

    @InjectMocks
    private CustomerService customerService;

    @Test
    public void testRegisterCustomer() {
        // Given
        String name = "Ramesh Fadatare";
        String email = "ramesh.fadatare@example.com";

        // When
        customerService.registerCustomer(name, email);

        // Then
        verify(customerRepository).saveCustomer(any(Customer.class));
    }

    @Test
    public void testGetCustomerByEmail() {
        // Given
        String email = "anita.patil@example.com";
        Customer customer = new Customer("Anita Patil", email);
        when(customerRepository.findCustomerByEmail(anyString())).thenReturn(customer);

        // When
        Customer result = customerService.getCustomerByEmail(email);

        // Then
        assertNotNull(result);
        assertEquals("Anita Patil", result.getName());
        assertEquals(email, result.getEmail());
    }

    @Test
    public void testGetAllCustomers() {
        // Given
        Customer customer1 = new Customer("Ravi Kumar", "ravi.kumar@example.com");
        Customer customer2 = new Customer("Rajesh Verma", "rajesh.verma@example.com");
        List<Customer> customers = Arrays.asList(customer1, customer2);
        when(customerRepository.findAllCustomers()).thenReturn(customers);

        // When
        List<Customer> result = customerService.getAllCustomers();

        // Then
        assertNotNull(result);
        assertEquals(2, result.size());
        assertEquals("Ravi Kumar", result.get(0).getName());
        assertEquals("Rajesh Verma", result.get(1).getName());
    }

    @Test
    public void testSaveCustomerWithCustomMatcher() {
        // Given
        String name = "Anjali Sharma";
        String email = "anjali.sharma@example.com";
        Customer customer = new Customer(name, email);

        // When
        customerService.registerCustomer(name, email);

        // Then
        verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Anjali Sharma")));
    }
}

Explanation

  1. Creating Mocks with @Mock:

    • The @Mock annotation creates a mock instance of the CustomerRepository interface. This mock instance can be used to simulate the behavior of the CustomerRepository in a controlled way.
  2. Injecting Mocks with @InjectMocks:

    • The @InjectMocks annotation injects the mock CustomerRepository into the CustomerService instance to provide a controlled test environment. This allows the CustomerService methods to be tested in isolation from the actual CustomerRepository implementation.
  3. Using ArgumentMatchers:

    • any(): The verify(customerRepository).saveCustomer(any(Customer.class)); method checks if the saveCustomer method was called on the CustomerRepository with any Customer object.
    • anyString(): The when(customerRepository.findCustomerByEmail(anyString())).thenReturn(customer); method configures the mock CustomerRepository to return a specific Customer object when the findCustomerByEmail method is called with any String value.
    • argThat(): The verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Anjali Sharma"))); method checks if the saveCustomer method was called on the CustomerRepository with a Customer object whose name is "Anjali Sharma".

Additional Scenarios

Scenario: Verifying Method Call with Multiple Argument Matchers

In this scenario, we will demonstrate how to verify a method call with multiple argument matchers using ArgumentMatchers.

@Test
public void testSaveCustomerWithMultipleMatchers() {
    // Given
    String name = "Rajesh Verma";
    String email = "rajesh.verma@example.com";

    // When
    customerService.registerCustomer(name, email);

    // Then
    verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Rajesh Verma") && argument.getEmail().equals("rajesh.verma@example.com")));
}

Explanation

  1. Using Multiple Argument Matchers:
    • The verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Rajesh Verma") && argument.getEmail().equals("rajesh.verma@example.com"))); method checks if the saveCustomer method was called on the CustomerRepository with a Customer object whose name is "Rajesh Verma" and email is "rajesh.verma@example.com".

Conclusion

ArgumentMatchers in Mockito simplifies the verification and stubbing of method calls on mock objects for unit testing. By using ArgumentMatchers, you can handle flexible argument matching, ensuring that your code interacts with its dependencies as expected. This step-by-step guide demonstrated how to effectively use ArgumentMatchers in your unit tests, covering different scenarios to ensure comprehensive testing of the CustomerService class.

Related Mockito Methods

Mockito mock()
Mockito spy()
Mockito when()
Mockito thenThrow()
Mockito verify()
Mockito times()
Mockito never()
Mockito any()
Mockito eq()
Mockito inOrder()
Mockito doReturn()
Mockito doThrow()
Mockito doAnswer()
Mockito timeout()
Mockito ArgumentMatchers

Comments