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