Mockito可以验证参数是否具有某些属性/字段?

Cap*_*Man 40 java unit-testing mocking mockito

说我在嘲笑这门课 Foo

class Foo {
  public void doThing(Bar bar) {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

这是 Bar

class Bar {
  private int i;
  public int getI() { return i; }
  public void setI(int i) { this.i = i; }
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用Mockito的验证功能来查看是否Foo#doThing(Bar)在具有特定实例Bar或任何 Barwith 的模拟上调用Mockito.any(Bar.class),但是有没有办法确保它被任何调用Bar但具有特定值i或者Bar#getI()

我所知道的可能是:

Foo mockedFoo = mock(Foo.class);
Bar someBar = mock(Bar.class);
...
verify(mockedFoo).doThing(someBar);
verify(mockedFoo).doThing(any(Bar.class);
Run Code Online (Sandbox Code Playgroud)

我想知道的是,是否有办法验证一个Bar关于它的特定事物是否作为参数传递.

Nic*_*olò 61

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

verify(mockedFoo).doThing(argThat((Bar aBar) -> aBar.getI() == 5));
Run Code Online (Sandbox Code Playgroud)

这是因为从Mockito 2.1.0开始,它ArgumentMatcher是一个功能界面.

  • @MarioTrucco显式语法是`(Bar aBar) - > aBar.getI()== 5`.也许这样输入会帮助你的IDE搞清楚吗? (3认同)
  • 如果此测试失败,将导致非常难以理解的错误消息。特别是当您多次调用整个“mockedFoo”模拟对象时。所以我更喜欢@youranos /sf/answers/3478159611/ 答案。让“ArgumentCaptor”具有许多显式的“assertEquals”会更加透明。 (2认同)

Cap*_*Man 16

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


我在写这个问题时找到了答案.

是的你可以.而不是使用any(Bar.class)你需要实现自己的实例ArgumentMatcher<T>和使用Mockito#argThat(Matcher),例如,说我们要检查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;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后验证如下: 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后验证如下: verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));


更新:因为徽章而在我的队列中弹出,看到了一些改进的空间.

我试过这个并且它有效.你可以使用一个更清晰的lambda表达式(如果你不介意至少未经检查的强制转换警告).

唯一的问题是,argThat接受Hamcrest Matcher这是不是一个@FunctionalInterface.幸运的是,Mockito ArgumentMatcher是一个扩展它的抽象类,只有一个抽象方法.

在您的测试(或一些常见位置)中,制作如下方法

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);
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

现在,在您的测试中,您可以使用lambda表达式:

verify(mockedFoo).doThing(argThat(matches( (Bar arg) -> arg.getI() == 5 )));
Run Code Online (Sandbox Code Playgroud)


yur*_*s87 5

如果不能使用Mockito 2+,则也可以使用good old ArgumentCaptor。不过,它会更加冗长:

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