Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon How do I create a custom ArgumentMatcher for a complex object


How do I create a custom ArgumentMatcher for a complex object


Creating a custom `ArgumentMatcher` for a complex object in Mockito allows you to define how arguments should be matched during stubbing or verification. Here's a step-by-step guide on how to create one:

1. Define the Complex Object

First, you need a complex object that you want to match. For example, let's say you have a `CarFilter` class with properties like `color` and `doorNumber`.

java
public class CarFilter {
    private String color;
    private int doorNumber;

    // Getters and setters
    public String getColor() { return color; }
    public void setColor(String color) { this.color = color; }
    public int getDoorNumber() { return doorNumber; }
    public void setDoorNumber(int doorNumber) { this.doorNumber = doorNumber; }
}

2. Create a Custom ArgumentMatcher

You'll extend `org.mockito.ArgumentMatcher` to create a custom matcher. This class will define how to match instances of `CarFilter`.

java
import org.mockito.ArgumentMatcher;

public class CarFilterMatcher extends ArgumentMatcher {
    private String expectedColor;
    private Integer expectedDoorNumber; // Use Integer to allow null for any()

    public CarFilterMatcher(String color, Integer doorNumber) {
        this.expectedColor = color;
        this.expectedDoorNumber = doorNumber;
    }

    @Override
    public boolean matches(Object argument) {
        if (!(argument instanceof CarFilter)) {
            return false;
        }
        
        CarFilter filter = (CarFilter) argument;
        
        boolean colorMatches = expectedColor == null || expectedColor.equals(filter.getColor());
        boolean doorNumberMatches = expectedDoorNumber == null || expectedDoorNumber.equals(filter.getDoorNumber());
        
        return colorMatches && doorNumberMatches;
    }

    @Override
    public void describeTo(org.hamcrest.Description description) {
        description.appendText("CarFilter with color ").appendValue(expectedColor);
        if (expectedDoorNumber != null) {
            description.appendText(" and door number ").appendValue(expectedDoorNumber);
        } else {
            description.appendText(" and any door number");
        }
    }
}

3. Use the Custom ArgumentMatcher

Now, you can use this custom matcher in your Mockito stubs or verifications.

java
// Example usage in stubbing
CarFinder carFinderMock = mock(CarFinder.class);
when(carFinderMock.find(argThat(new CarFilterMatcher("Red", null)))).thenReturn(car1);

// Example usage in verification
verify(carFinderMock).find(argThat(new CarFilterMatcher("Red", null)));

In this example, `CarFilterMatcher` allows you to match `CarFilter` instances based on specific criteria, such as color and door number, while also allowing for "any" values by passing `null`.

4. Considerations

- Equals Method: If you're using Mockito's built-in matchers like `eq()`, ensure that the object's `equals()` method is properly implemented. However, with custom matchers, you have full control over how matching is performed.
- ArgumentCaptor: Sometimes, using an `ArgumentCaptor` might be more suitable if you need to verify the argument values directly rather than relying on a custom matcher.
- Hamcrest Integration: Mockito integrates well with Hamcrest matchers, which can be used alongside custom matchers for more complex scenarios.

By following these steps, you can effectively create and use custom `ArgumentMatcher` instances for complex objects in your Mockito tests.

Citations:
[1] https://www.javadoc.io/static/org.mockito/mockito-all/1.10.9/org/mockito/ArgumentMatcher.html
[2] https://stackoverflow.com/questions/15140467/mockito-stub-method-with-complex-object-as-a-parameter
[3] https://javadoc.io/static/org.mockito/mockito-core/1.9.0/org/mockito/ArgumentMatcher.html
[4] https://groups.google.com/g/mockito/c/LJyOAJYR-54
[5] https://stackoverflow.com/questions/56559146/junit-using-eq-argument-matcher-vs-passing-string-directly/56572275
[6] https://dzone.com/articles/mockito-custom-answers
[7] https://stackoverflow.com/questions/39398513/how-can-i-create-a-custom-argumentmatcher-that-accepts-arguments-from-other-matc
[8] https://github.com/mockito/mockito/issues/1753