Captain Man Captain Man - 1 month ago 11
Java Question

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.

Answer

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 an upvote and saw some room for improvement.

I haven't tried this but it should work. You should be able to use a lambda expression which is a lot cleaner.

verify(mockedFoo).doThing(argThat( arg -> ((Bar) arg).getI() == 5 ));

I am not sure if Bar arg -> arg.getI() == 5 will work, because it looks like the ArgumentMatcher<T>#matches took an Object, not T. I can't be sure because those old links are now dead (R.I.P. Google Code) and I don't feel like looking it up. It will take you less time to try it than for me to look it up. Feel free to edit this paragraph out and change the above if you wish to do the research.