Java Mockito和Hamcrest:如何验证集合参数的调用?
我遇到了Mockito和Hamcrest的泛型问题 请假定以下接口:Java Mockito和Hamcrest:如何验证集合参数的调用?,java,generics,mockito,hamcrest,Java,Generics,Mockito,Hamcrest,我遇到了Mockito和Hamcrest的泛型问题 请假定以下接口: public interface Service { void perform(Collection<String> elements); } 因此,我想验证我的业务逻辑是否实际使用一个按顺序包含“a”和“b”的集合调用了服务 但是,contains(…)的返回类型是Matcher,您可以有自己的java.util.Collection实现,并覆盖equals方法,如下所示 public interfac
public interface Service {
void perform(Collection<String> elements);
}
因此,我想验证我的业务逻辑是否实际使用一个按顺序包含“a”和“b”的集合调用了服务
但是,
contains(…)
的返回类型是Matcher,您可以有自己的java.util.Collection实现,并覆盖equals方法,如下所示
public interface Service {
void perform(Collection<String> elements);
}
@Test
public void testName() throws Exception {
Service service = mock(Service.class);
service.perform(new HashSet<String>(Arrays.asList("a","b")));
Mockito.verify(service).perform(Matchers.eq(new CollectionVerifier<String>(Arrays.asList("a","b"))));
}
public class CollectionVerifier<E> extends ArrayList<E> {
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;
}
}
公共接口服务{
无效执行(收集元素);
}
@试验
public void testName()引发异常{
服务=模拟(Service.class);
perform(新的HashSet(Arrays.asList(“a”、“b”));
Mockito.verify(service).perform(Matchers.eq(newcollectionverifier(Arrays.asList(“a”,“b”))));
}
公共类CollectionVerifier扩展了ArrayList{
公共收集验证程序(){
}
公开收集验证人(最终收集){
集合其他=(集合)o;
返回this.size()==other.size()&&this.containsAll(other);
}
返回false;
}
}
如果遇到这种情况,请记住,您可以编写一个非常小的可重用适配器
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);
}
};
}
验证(服务)。执行(argThat(isACollectionThat(包含(“foo”、“bar”))));
私有静态匹配器是一个集合(
final Matcher为什么不使用预期参数进行验证,假设列表只包含两项,例如:
final List<String> expected = Lists.newArrayList("a", "b");
verify(service).perform(expected);
final List expected=Lists.newArrayList(“a”、“b”);
验证(服务)。执行(预期);
虽然我原则上同意Eugen的观点,但我认为依靠equals进行字符串比较是可以接受的……此外,包含的匹配器使用equals进行比较。您只需编写
verify(service).perform((Collection<String>) Matchers.argThat(contains("a", "b")));
verify(service).perform((Collection)Matchers.argThat(包含(“a”、“b”));
从编译器的角度来看,这是将Iterable
转换为集合
,这很好,因为后者是前者的子类型。在运行时,argThat
将返回null
,这样就可以在不使用ClassCastException
的情况下将其传递到perform
但是,匹配器会进入Mockito的内部参数结构进行验证,这就是arg所做的。作为替代方法,可以改变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"));
您可以将自己的lambda作为ArgumentMatcher
when(myClass.myMethod(argThat(arg -> arg.containsAll(asList(1,2))))
.thenReturn(...);
将Matcher
强制转换为Matcher
?这肯定不会编译…如果您知道集合类型,并且集合具有正确的equals
实现,那么您可以使用包含“a”和“b”的集合实例调用verify
。但这将是一个糟糕的测试-首先,您将分解实现细节你必须使用正确的equals方法。所以我会毫无疑问地使用参数捕捉器。我不知道要避免参数捕捉器,你会走多远,但也许你可以实现一个自定义匹配器,如IsListOfTwoElements
:谢谢你的建议。毫不奇怪,我不喜欢它们中的任何一个。我想我必须使用这些参数毕竟是ument captors.:-(您无法验证集合
上的“顺序”,因为集合
不保证是有序集合。您确定是指集合而不是列表
?@David您应该:)欢迎您在事后访问,以获得适当的信任。感谢您的回答以及Mockito matchers返回null
的提示。如果@DavidWallace确实添加了他的评论作为回复,我将接受他的回答。希望您不会介意。:-@Philupp这是公平的!他只是回答了,FWIW,所以我正在编辑我的,以避免重复。contains
返回一个匹配项,其中包含您使用的内容?我只查找包含(字符串子字符串)的数据,以便使其保持最新;使用Kotlin执行此操作的一个简单方法是:verify(service).perform(argThat{“b”==this[“a”]})
@barracuda317他们正在使用org.hamcrest.Matchers.contains()。我设法用org.hamcrest.CoreMatchers.hasItems()解决了这个问题。由于mockito 2.1.0,您需要使用MockitoHamcrest.argThat。令人惊叹的!如果要检查空集合,可以使用argThat(collection::isEmpty)
。
assertThat(captor.getValue()).containsExactly("a", "b");
when(myClass.myMethod(argThat(arg -> arg.containsAll(asList(1,2))))
.thenReturn(...);