簡體   English   中英

Mockito/JMockit & Hamcrest 匹配器:如何驗證列表/集合?

[英]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 類

  1. <E> Matcher<Iterable<? extends E>> contains(E... items)
  2. <E> Matcher<Iterable<? extends E>> contains(Matcher<? super E> itemMatcher)
  3. <E> Matcher<Iterable<? extends E>> contains(Matcher<? super E>... itemMatchers)
  4. <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類型的EList<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM