Mockito和Hamcrest:如何validationCollection参数的调用?

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

请假设以下界面:

public interface Service { void perform(Collection elements); } 

以下测试片段:

 Service service = mock(Service.class); // ... perform business logic verify(service).perform(Matchers.argThat(contains("a", "b"))); 

所以我想validation我的业务逻辑实际上是用这个顺序包含“a”和“b”的集合调用服务。

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

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

有什么建议! 谢谢!

你可以写

 verify(service).perform((Collection) Matchers.argThat(contains("a", "b"))); 

从编译器的角度来看,这是将Iterable转换为Collection ,这很好,因为后者是前者的子类型。 在运行时, argThat将返回null ,因此可以传递给没有ClassCastException perform 。 关于它的重要一点是,匹配器进入Mockito的内部validation论证结构,这就是argThat作用。

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

 verify(service).perform(argThat(isACollectionThat(contains("foo", "bar")))); private static  Matcher> isACollectionThat( final Matcher> matcher) { return new BaseMatcher>() { @Override public boolean matches(Object item) { return matcher.matches(item); } @Override public void describeTo(Description description) { matcher.describeTo(description); } }; } 

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

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

 @SuppressWarnings("unchecked") // needed because of `List.class` is not a thing // suppression can be worked around by using @Captor on a field ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(service).perform(captor.capture()); assertThat(captor.getValue(), contains("a", "b")); 

为什么不只是用预期的参数进行validation,假设列表只包含两个项目,例如:

 final List expected = Lists.newArrayList("a", "b"); verify(service).perform(expected); 

虽然我原则上同意Eugen,但我认为依赖于equals进行字符串比较是可以接受的…此外, contains匹配器使用equals进行比较。

您可以拥有自己的java.util.Collection实现并覆盖如下所示的equals方法。

 public interface Service { void perform(Collection elements); } @Test public void testName() throws Exception { Service service = mock(Service.class); service.perform(new HashSet(Arrays.asList("a","b"))); Mockito.verify(service).perform(Matchers.eq(new CollectionVerifier(Arrays.asList("a","b")))); } public class CollectionVerifier extends ArrayList { public CollectionVerifier() { } public CollectionVerifier(final Collection c) { super(c); } @Override public boolean equals(final Object o) { if (o instanceof Collection) { Collection other = (Collection) o; return this.size() == other.size() && this.containsAll(other); } return false; } }