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

时间:2015-10-26 17:05:09

标签: java generics mockito jmockit hamcrest

这个2013 post on SO询问如何使用Hamcrest匹配器来验证Mockito中的列表/集合调用。接受的解决方案是将Matcher转换为(Collection)。

我试图做类似的事情,但遇到了类强制转换错误。我不确定我是否在滥用Hamcrest匹配器,或者这种用法根本不受Mockito的支持。就我而言,我试图使用Matchers列表作为我的论点:

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>

我做的事情不受支持吗?

4 个答案:

答案 0 :(得分:4)

正如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<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")))));

答案 1 :(得分:2)

我认为这是由于哈姆克雷斯特的一种令人讨厌的含糊之处,它在Matchers class上有:

  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)
  5. 这是正确的,取决于你是否通过Hamcrest项目,匹配器,varargs匹配器列表或匹配器列表,你会得到不同的行为。因为Java没有厌恶匹配Hamcrest匹配器的列表,所以有一个语句有很多机会匹配多个重载,并且它们之间的选择是由{{3}中令人眼花缭乱的类型代数决定的最具体的重载。 }。

    我认为你打算在第4项上面通过contains列出Matcher<E>Matcher<String>)并找回Matcher<Iterable<? extends String>> - 但编译器会看到它作为#1 - 传递contains 类型EList<Matcher<String>>)并获得Matcher<Iterable<? extends List<Matcher<String>>>>

    有几种解决方法,我尚未测试:

    • Matcher提取到变量,您可以使用像contains这样的Hamcrest匹配器,但不能使用像argThat这样的Mockito匹配器:

      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")))));
      

答案 2 :(得分:2)

最好的方法是使用标准的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.
    }};
}

答案 3 :(得分:2)

我更喜欢使用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"))
            )
        )
    );