Hi I am writing a custom matcher to validate two different object, and my code is:
public class DetailsMatcher extends ArgumentMatcher <Details> {
private final Details expected;
private DetailsMatcher(Details expected) {
this.expected = expected;
}
@Override
public boolean matches(Object actual) {
return ((Request) actual).getId().equals(expected.getId());
}
@Override
public void describeTo(Description description) {
description.appendText(expected == null ? null : expected.toString());
}
public static Details valueObjectEq(Details expected) {
return argThat(new DetailsMatcher(expected));
}
}
And I want to use it in my test as:
assertThat(requestModel, DetailsMatcher.valueObjectEq(response));
But these are two different object so that it doesn't work. I don't want to change my objects just for testing purpose, so do we have some api like assertThat, which allows passing different object to simply relies on the output of matches, rather than forcing the actual and expected of same type?
Be careful to keep Mockito matchers and Hamcrest matchers separate. A Hamcrest or Hamcrest-style matcher is an object instance, like your new DetailsMatcher(expected)
above. With the call to argThat
, your valueObjectEq
appears to return a Details
object, but that's not true: It will return null
and Mockito will save an object on an internal stack. Consequently, you should never call argThat
outside of a call to when
or verify
.
Instead, change your DetailsMatcher to be of type ArgumentMatcher<Request>
to indicate that your Matcher can work on any Request
, not just Details
, and make the constructor public so you can call it like this:
assertThat(requestModel, new DetailsMatcher(orderResponse));
// or with a static helper method you write:
assertThat(requestModel, matchesValueObject(orderResponse));
A few other notes:
Matcher.matches
should never throw an exception, but it will do so here if you pass in a non-Request. Check with instanceof
and return false
if the argument isn't a Matcher.org.hamcrest.Matcher
. If you want to keep using the Matcher for its Hamcrest properties, you may need to have it extend both, or just make it a Hamcrest matcher and adjust valueObjectEq
to call MockitoHamcrest.argThat instead.Though the error message won't be as clear, you can always call the method manually:
assertTrue(new DetailsMatcher(orderReponse).matches(requestModel));
See also: