Can Mockito verify an argument has certain properties/fields?
Say I am mocking this class Foo
class Foo {
public void doThing(Bar bar) {
// ...
}
}
and this is Bar
class Bar {
private int i;
public int getI() { return i; }
public void setI(int i) { this.i = i; }
}
I know I can use Mockito's verify functionality to see if Foo#doThing(Bar)
was called on the mock with a specific instance of Bar
or any Bar
with Mockito.any(Bar.class)
, but is there some way to ensure it was called by any Bar
but with a specific value for i
or Bar#getI()
?
What I know is possible:
Foo mockedFoo = mock(Foo.class);
Bar someBar = mock(Bar.class);
...
verify(mockedFoo).doThing(someBar);
verify(mockedFoo).doThing(any(Bar.class);
What I want to know is if there is a way to verify that a Bar
with particular things true about it was passed as an argument.
In Mockito 2.1.0 and up with Java 8 you can pass the lambda to argThat out of the box so that one does not need a custom argument matchers. For the example in the OP would be:
verify(mockedFoo).doThing(argThat((Bar aBar) -> aBar.getI() == 5));
This is because as of Mockito 2.1.0, ArgumentMatcher
is a functional interface.
If you are using Mockito 2.1.0 or above and Java 8 or above, see this answer instead, it's much simpler now.
I found the answer while writing the question.
Yes, you can. Instead of using any(Bar.class)
you'll need to implement your own instance of ArgumentMatcher<T>
and use Mockito#argThat(Matcher)
, for example, say we want to check that i
is 5...
// in the test (could also be outside)
private static final class BarIs5 extends ArgumentMatcher<Bar> {
@Override
public boolean matches(Object argument) {
return ((Bar) argument).getI() == 5;
}
}
Then verify like so: verify(mockedFoo).doThing(argThat(new BarIs5()));
Kick it up a notch by adding constructor parameters!
private static final class BarIsWhat extends ArgumentMatcher<Bar> {
private final int i;
public BarIsWhat(int i) {
this.i = i
}
@Override
public boolean matches(Object argument) {
return ((Bar) argument).getI() == i;
}
}
Then verify like so: verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));
Update: This popped in my queue because of a badge and saw some room for improvement.
I have tried this and it works. You can sort of use a lambda expression which is a lot cleaner (if you don't mind unchecked cast warnings at least).
The only issue is that argThat
accepts a Hamcrest Matcher
which is not a @FunctionalInterface
. Luckily, Mockito's ArgumentMatcher
is an abstract class that extends it and only has a single abstract method.
In your test (or some common location) make a method like below
private static <T> ArgumentMatcher<T> matches(Predicate<T> predicate) {
return new ArgumentMatcher<T>() {
@SuppressWarnings("unchecked")
@Override
public boolean matches(Object argument) {
return predicate.test((T) argument);
}
};
}
Now, in your test you can do this to use a lambda expression:
verify(mockedFoo).doThing(argThat(matches( (Bar arg) -> arg.getI() == 5 )));
If using Mockito 2+ is not an option, you can also use good old ArgumentCaptor
. It'll be a bit more verbose though:
ArgumentCaptor<Long> siteIdCaptor = ArgumentCaptor.forClass(Long.class);
verify(repository).findBySiteId(siteIdCaptor.capture());
assertEquals(15, siteIdCaptor.getValue().longValue());