Mockito和Hamcrest:如何验证Collection参数的调用?

Phi*_*das 35 java generics hamcrest mockito

我遇到了Mockito和Hamcrest的仿制问题.

请假设以下界面:

public interface Service {
    void perform(Collection<String> elements);
}
Run Code Online (Sandbox Code Playgroud)

以下测试片段:

Service service = mock(Service.class);

// ... perform business logic

verify(service).perform(Matchers.argThat(contains("a", "b")));
Run Code Online (Sandbox Code Playgroud)

所以我想验证我的业务逻辑实际上是用一个包含"a"和"b"的集合来调用服务.

但是,返回类型contains(...)Matcher<Iterable<? extends E>>,所以在我的情况下Matchers.argThat(...)返回Iterable<String>,这自然不适用于所需的Collection<String>.

我知道我可以使用Hamcrest hasItem和Mockito中提出的参数捕获器验证不一致,但我非常愿意不这样做.

有什么建议!谢谢!

Daw*_*ica 29

你可以写

verify(service).perform((Collection<String>) Matchers.argThat(contains("a", "b")));
Run Code Online (Sandbox Code Playgroud)

但从编译器的角度来看,这是铸造Iterable<String>Collection<String>这是很好的,因为后者是前者的一个亚型.在运行时,argThat将返回null,以便可以传递给perform没有ClassCastException.关于它的重要一点是,匹配器进入Mockito的内部结构参数进行验证,这就是做什么的argThat.

  • 你包含哪些?我只有finde包含(String substring) (5认同)
  • 从mockito 2.1.0开始,你需要使用MockitoHamcrest.argThat。https://javadoc.io/doc/org.mockito/mockito-core/2.2.9/org/mockito/ArgumentMatcher.html (2认同)

Jef*_*ica 7

如果您遇到类似这样的情况,请记住您可以编写一个非常小的可重用适配器.

verify(service).perform(argThat(isACollectionThat(contains("foo", "bar"))));

private static <T> Matcher<Collection<T>> isACollectionThat(
    final Matcher<Iterable<? extends T>> matcher) {
  return new BaseMatcher<Collection<T>>() {
    @Override public boolean matches(Object item) {
      return matcher.matches(item);
    }

    @Override public void describeTo(Description description) {
      matcher.describeTo(description);
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

请注意,David的上述解决方案,使用强制转换,是最简单的正确答案.

  • 哈哈,其中一天,我将学会使用"答案"框而不是"评论"框. (2认同)

TWi*_*Rob 7

作为替代方案,可以将方法改为ArgumentCaptor:

@SuppressWarnings("unchecked") // needed because of `List<String>.class` is not a thing
// suppression can be worked around by using @Captor on a field
ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);

verify(service).perform(captor.capture());
assertThat(captor.getValue(), contains("a", "b"));
Run Code Online (Sandbox Code Playgroud)


deh*_*asi 6

您可以将自己的 lambda 作为 ArgumentMatcher

when(myClass.myMethod(argThat(arg -> arg.containsAll(asList(1,2))))
    .thenReturn(...);
Run Code Online (Sandbox Code Playgroud)