In a JUnit test using Mockito, I have an argThat
matcher like this:
Mockito.argThat(obj -> {
return "ok".equals(obj.getStuff()) ||
"ok2".equals(obj.getStuff2()) ||
"ok3".equals(obj.getStuff3()) ||
"ok4".equals(obj.getStuff4());
});
When the test fails, it generically fails and it’s hard to figure out which condition caused the failure. I tried adding assertions inside argThat
for better clarity:
Mockito.argThat(obj -> {
Assert.assertEquals("ok", obj.getStuff());
Assert.assertEquals("ok2", obj.getStuff2());
Assert.assertEquals("ok3", obj.getStuff3());
Assert.assertEquals("ok4", obj.getStuff4());
return true;
});
This gives clearer failure messages, but it feels a bit “wonky.” Is there a better way to handle this while using mockito argThat
?
Putting assertions inside argThat
is generally not recommended. argThat
is meant for matching arguments, not performing test assertions.
A cleaner approach is to return a boolean from your matcher and then verify with separate assertions afterward:
Mockito.verify(mock).someMethod(Mockito.argThat(obj ->
"ok".equals(obj.getStuff()) &&
"ok2".equals(obj.getStuff2()) &&
"ok3".equals(obj.getStuff3()) &&
"ok4".equals(obj.getStuff4())
));
MyObject captured = argumentCaptor.getValue();
assertEquals("ok", captured.getStuff());
assertEquals("ok2", captured.getStuff2());
assertEquals("ok3", captured.getStuff3());
assertEquals("ok4", captured.getStuff4());
This way, failure messages are clear, and you’re not misusing argThat
.
Another approach is to define a custom ArgumentMatcher
. This gives better readability and more detailed failure messages without putting assertions inside the lambda:
class MyObjectMatcher implements ArgumentMatcher<MyObject> {
@Override
public boolean matches(MyObject obj) {
return "ok".equals(obj.getStuff()) &&
"ok2".equals(obj.getStuff2()) &&
"ok3".equals(obj.getStuff3()) &&
"ok4".equals(obj.getStuff4());
}
@Override
public String toString() {
return "Expected MyObject with stuff='ok', stuff2='ok2', stuff3='ok3', stuff4='ok4'";
}
}
Mockito.verify(mock).someMethod(Mockito.argThat(new MyObjectMatcher()));
The toString()
method helps make failure messages more descriptive when the matcher fails.
Instead of putting assertions inside argThat
, you can capture the argument and assert afterward. This often produces the clearest test reports:
ArgumentCaptor<MyObject> captor = ArgumentCaptor.forClass(MyObject.class);
Mockito.verify(mock).someMethod(captor.capture());
MyObject obj = captor.getValue();
assertEquals("ok", obj.getStuff());
assertEquals("ok2", obj.getStuff2());
assertEquals("ok3", obj.getStuff3());
assertEquals("ok4", obj.getStuff4());
I like this approach because each failed assertion clearly indicates which field caused the failure, and it keeps Mockito usage idiomatic.