Mockito 可以验证一个参数是否具有特定的 properties/fields?

Can Mockito verify an argument has certain properties/fields?

说我在嘲笑这个classFoo

class Foo {
  public void doThing(Bar bar) {
    // ...
  }
}

这是Bar

class Bar {
  private int i;
  public int getI() { return i; }
  public void setI(int i) { this.i = i; }
}

我知道我可以使用 Mockito 的验证功能来查看是否使用 Bar 的特定实例或任何带有 Mockito.any(Bar.class)Bar 在 mock 上调用了 Foo#doThing(Bar)但是有什么方法可以确保它被任何 Bar 调用但具有 iBar#getI() 的特定值?

我所知道的是可能的:

Foo mockedFoo = mock(Foo.class);
Bar someBar = mock(Bar.class);
...
verify(mockedFoo).doThing(someBar);
verify(mockedFoo).doThing(any(Bar.class);

我想知道的是,是否有一种方法可以验证 Bar 的特定内容是否作为参数传递。

如果您使用的是 Mockito 2.1.0 或更高版本以及 Java 8 或更高版本,请参阅 答案,现在更简单了。


我在写问题的时候找到了答案。

是的,你可以。您需要实现自己的 ArgumentMatcher<T> and use Mockito#argThat(Matcher) 实例,而不是使用 any(Bar.class),例如,假设我们要检查 i 是 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;
  }
}

然后像这样验证:verify(mockedFoo).doThing(argThat(new BarIs5()));


通过添加构造函数参数使其更上一层楼!

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;
  }
}

然后像这样验证:verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));


更新: 由于徽章而出现在我的队列中,并且看到了一些改进的空间。

我试过了,效果很好。您可以使用更简洁的 lambda 表达式(如果您至少不介意未经检查的转换警告)。

唯一的问题是 argThat 接受 Hamcrest Matcher,而 而不是 @FunctionalInterface。幸运的是,Mockito 的 ArgumentMatcher 是一个扩展它的抽象 class,并且只有一个抽象方法。

在您的测试(或一些常见位置)中创建如下方法

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);
    }
  };
}

现在,在您的测试中,您可以使用 lambda 表达式执行此操作:

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

Mockito 2.1.0 和 Java 8 中,您可以开箱即用地将 lambda 传递给 argThat 这样就不需要自定义参数匹配器.对于 OP 中的示例将是:

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

这是因为从 Mockito 2.1.0 开始,ArgumentMatcher 是一个函数式接口。

如果不能使用 Mockito 2+,您也可以使用旧的 ArgumentCaptor。不过会有点冗长:

ArgumentCaptor<Long> siteIdCaptor = ArgumentCaptor.forClass(Long.class);
verify(repository).findBySiteId(siteIdCaptor.capture());
assertEquals(15, siteIdCaptor.getValue().longValue());

如果您正在寻找 when.. then.. 语法,这应该是一个可行的替代方法:

doReturn(valueToReturn).when(mockedFoo).doThing(argThat((Bar aBar) -> aBar.getI() == 5));