Using custom argument matchers for complex argument matching scenarios

In the world of unit testing, Mockito is a popular Java framework that allows developers to easily create mock objects, stub method calls, and verify interactions between objects. One powerful feature of Mockito is its ability to match method arguments, ensuring that the correct input is passed to the mocked object. While Mockito provides several built-in argument matchers, there are scenarios where custom argument matchers are needed for complex matching requirements. This article explores how to leverage custom argument matchers in Mockito for such scenarios.

What are argument matchers?

Before diving into custom argument matchers, let's quickly review argument matchers in Mockito. Argument matchers are used to define flexible and precise criteria for matching method arguments in Mockito. They are essential when you don't want to provide fixed values for arguments but rather want to verify that certain conditions are met.

Standard argument matchers provided by Mockito include any(), eq(), isNull(), isNotNull(), anyInt(), anyString(), and many others. These matchers cover most common scenarios, but sometimes more complex matching logic is required.

Creating custom argument matchers

Custom argument matchers are created by implementing the ArgumentMatcher interface provided by Mockito. This interface has a single method matches(), which takes the argument passed to a method invocation and returns true or false based on the matching criteria.

Let's consider an example where we have a CustomerService class with a createCustomer() method that takes a Customer object as an argument. The Customer class has properties such as name, age, and address. Now, suppose we want to ensure that the createCustomer() method is only called with customers who are above a certain minimum age. We can create a custom argument matcher to achieve this.

public class MinimumAgeMatcher implements ArgumentMatcher<Customer> {
    private int minimumAge;
    
    public MinimumAgeMatcher(int minimumAge) {
        this.minimumAge = minimumAge;
    }
    
    @Override
    public boolean matches(Customer customer) {
        return customer.getAge() >= minimumAge;
    }
}

In the above code snippet, we create the MinimumAgeMatcher class that implements the ArgumentMatcher<Customer> interface. We initialize the minimumAge value through the constructor. The matches() method checks if the age of the provided Customer object is greater than or equal to the minimum age. By returning true or false, we indicate whether the argument matches the criteria.

Using custom argument matchers

To use our custom argument matcher, we need to instruct Mockito to apply it when verifying or stubbing method invocations. We can do this using the argThat() method, which allows us to pass an instance of our custom argument matcher.

CustomerService customerService = Mockito.mock(CustomerService.class);

int minimumAge = 18;
Customer customer = new Customer("John Doe", 25, "123 Main St.");
customerService.createCustomer(customer);

Mockito.verify(customerService).createCustomer(Mockito.argThat(new MinimumAgeMatcher(minimumAge)));

In the above example, we create a mock object of the CustomerService class and call the createCustomer() method with a Customer object. We then use Mockito.verify() along with Mockito.argThat() to verify that the createCustomer() method is called with an argument that matches the criteria defined in the custom argument matcher.

By providing our custom argument matcher inside the argThat() method, Mockito will invoke our matches() method with the argument passed to the method. If the matches() method returns true, the verification passes; otherwise, it fails.

Conclusion

Custom argument matchers in Mockito play a crucial role when standard matchers are not sufficient for complex argument matching scenarios. By implementing the ArgumentMatcher interface, developers can define their own matching logic based on specific requirements. This provides flexibility and precision in verifying method invocations and ensuring the correct arguments are passed to mocked objects. Utilizing custom argument matchers can enhance the effectiveness of unit tests and improve overall code quality.


noob to master © copyleft