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