繁体   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