![](/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.