![](/img/trans.png)
[英]Hamcrest: how to use either/or combinable matchers with Collections
[英]Mockito/JMockit & Hamcrest matchers : How to verify Lists/Collections?
这篇2013 年关于 SO 的帖子询问了如何使用 Hamcrest 匹配器来验证 Mockito 中的列表/集合调用。 公认的解决方案是将 Matcher 转换为 (Collection)。
我正在尝试做类似的事情,但遇到了类转换错误。 我不确定我是否误用了 Hamcrest 匹配器,或者 Mockito 是否根本不支持这种用法。 就我而言,我试图使用匹配器列表作为我的参数:
static class Collaborator
{
void doSomething(Iterable<String> values) {}
}
@Test
public void usingMockito()
{
Collaborator mock = Mockito.mock(Collaborator.class);
mock.doSomething(Arrays.asList("a", "b"));
// legal cast
Mockito.verify(mock).doSomething((Collection<String>)argThat(Matchers.contains("a", "b")));
// legal cast
Mockito.verify(mock).doSomething((Collection<String>)argThat(Matchers.contains(Matchers.equalTo("a"), Matchers.equalTo("b"))));
// illegal cast!!! Cannot cast from Iterable<capture#3-of ? extends List<Matcher<String>>> to Collection<String>
Mockito.verify(mock).doSomething((Collection<String>)argThat(Matchers.contains(Arrays.asList(Matchers.equalTo("a"), Matchers.equalTo("b")))));
}
但我得到了演员错误:
Cannot cast from Iterable<capture#3-of ? extends List<Matcher<String>>> to Collection<String>
我在做一些不受支持的事情吗?
我更喜欢使用allOf
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
...
Mockito.verify(mock).doSomething(
argThat(
allOf(
hasItems(equalTo("a")),
hasItems(equalTo("b"))
)
)
);
正如 Jeff Bowman 已经指出的那样,问题在于编译器不知道 4 个contains
您尝试调用的方法中的哪一个。
您正在构建的列表
Arrays.asList(Matchers.equalTo("a"), Matchers.equalTo("b"))
是类型
List<Matcher<String>>
但是您要调用的contains
方法( <E> Matcher<Iterable<? extends E>> contains(List<Matcher<? super E>> itemMatchers)
)需要一个类型
List<Matcher<? super String>>
作为参数。 由于您的列表类型与预期类型不匹配,编译器实际上认为您正在尝试调用
<E> Matcher<Iterable<? extends E>> contains(E... items)
解决方案:给编译器它想要的东西。 创建一个List<Matcher<? super String>>
List<Matcher<? super String>>
而不是List<Matcher<String>>
:
List<Matcher<? super String>> matchersList = new ArrayList<>();
matchersList.add(Matchers.equalTo("a"));
matchersList.add(Matchers.equalTo("b"));
// no illegal cast anymore
Mockito.verify(mock).doSomething(
(Collection<String>) argThat(Matchers.contains(matchersList)));
编辑:
从他的评论中添加 Jeff Bowman 的内联解决方案,这使得可以使用问题中所述的Arrays.asList
:
Mockito.verify(mock).doSomething(
(Collection<String>) argThat(
Matchers.contains(
Arrays.<Matcher<? super String>> asList(
Matchers.equalTo("a"), Matchers.equalTo("b")
)
)
)
);
我相信这是由于 Hamcrest 中令人讨厌的歧义,它的Matchers 类:
<E> Matcher<Iterable<? extends E>> contains(E... items)
<E> Matcher<Iterable<? extends E>> contains(Matcher<? super E> itemMatcher)
<E> Matcher<Iterable<? extends E>> contains(Matcher<? super E>... itemMatchers)
<E> Matcher<Iterable<? extends E>> contains(List<Matcher<? super E>> itemMatchers)
没错,根据您是向 Hamcrest 传递项目、匹配器、匹配器的可变参数数组还是匹配器列表,您将获得不同的行为。 因为 Java 不反对匹配 Hamcrest 匹配器列表,所以一个语句有很多机会匹配多个重载,并且它们之间的选择是最具体的重载,由JLS 18.5.4 中令人眼花缭乱的类型代数确定.
我认为您打算使用上面的第 4 项Matcher<E>
contains
一个Matcher<E>
( Matcher<String>
) 列表并返回一个Matcher<Iterable<? extends String>>
Matcher<Iterable<? extends String>>
-但是编译器将其视为#1通contains
类型的值E
( List<Matcher<String>>
),并得到一个Matcher<Iterable<? extends List<Matcher<String>>>>
Matcher<Iterable<? extends List<Matcher<String>>>>
。
有几种解决方法,我还没有测试过:
将Matcher
提取到一个变量中,您可以使用 Hamcrest 匹配器(如contains
而不是 Mockito 匹配器(如argThat
:
Matcher<Iterable<String>> matchesAAndB = Matchers.contains( Arrays.asList(Matchers.equalTo("a"), Matchers.equalTo("b"))); Mockito.verify(mock).doSomething((Collection<String>)argThat(matchesAAndB));
明确选择 E:
Mockito.verify(mock).doSomething((Collection<String>)argThat( Matchers.<String>contains(Arrays.asList( Matchers.equalTo("a"), Matchers.equalTo("b")))));
最好的方法是使用标准的assertThat
方法(来自 Hamcrest 或 JUnit),它最适合任何 Hamcrest 匹配器。 使用 JMockit,您可以执行以下操作:
@Test
public void usingJMockit(@Mocked final Collaborator mock) {
mock.doSomething(asList("a", "b"));
new Verifications() {{
List<String> values;
mock.doSomething(values = withCapture());
// Now check the list of captured values using JUnit/Hamcrest:
assertThat(values, contains("a", "b"));
// Alternatively, could have used Asser4J, FEST Assert, etc.
}};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.