Java Mockito/JMockit&;Hamcrest matchers:如何验证列表/集合?

Java Mockito/JMockit&;Hamcrest matchers:如何验证列表/集合?,java,generics,mockito,jmockit,hamcrest,Java,Generics,Mockito,Jmockit,Hamcrest,这询问了如何使用Hamcrest匹配器来验证Mockito中的列表/集合调用。公认的解决方案是将匹配器强制转换为(集合) 我正在尝试做类似的事情,但遇到了类强制转换错误。我不确定我是否误用了Hamcrest matchers,或者Mockito不支持这种用法。在我的例子中,我尝试使用匹配器列表作为我的参数: static class Collaborator { void doSomething(Iterable<String> values) {} } @Test publ

这询问了如何使用Hamcrest匹配器来验证Mockito中的列表/集合调用。公认的解决方案是将匹配器强制转换为(集合)

我正在尝试做类似的事情,但遇到了类强制转换错误。我不确定我是否误用了Hamcrest matchers,或者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")))));
}
静态类协作器
{
void doSomething(Iterable值){}
}
@试验
使用mockito()的public void
{
Collaborator mock=Mockito.mock(Collaborator.class);
模拟剂量计(数组.asList(“a”,“b”));
//法定演员阵容
Mockito.verify(mock).doSomething((Collection)argThat(Matchers.contains)(“a”,“b”));
//法定演员阵容
Mockito.verify(mock).doSomething((集合)argThat(Matchers.contains(Matchers.equalTo(“a”)、Matchers.equalTo(“b”)));
//非法强制转换!!!无法从Iterable强制转换到集合
Mockito.verify(mock).doSomething((Collection)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>
无法从Iterable强制转换为集合

我在做一些不受支持的事情吗?

我认为这是因为Hamcrest中存在令人讨厌的模糊性,它有以下特点:


  • Matcher正如Jeff Bowman已经指出的,问题是编译器不知道4个
    中的哪一个包含您试图调用的
    方法

    您正在构建的列表

    Arrays.asList(Matchers.equalTo("a"), Matchers.equalTo("b"))
    
    是一种

    List<Matcher<String>>
    
    作为参数。由于列表类型与预期类型不匹配,编译器实际上认为您正在尝试调用

    <E> Matcher<Iterable<? extends E>> contains(E... items)
    

    最好的方法是使用标准的
    assertThat
    方法(来自Hamcrest或JUnit),这将最适合任何Hamcrest matcher。使用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.
        }};
    }
    
    @测试
    使用JMockit(@Mocked final Collaborator mock)的公共作废{
    模拟剂量(asList(“a”、“b”);
    新的核查(){{
    列表值;
    mock.doSomething(value=withCapture());
    //现在使用JUnit/Hamcrest检查捕获的值列表:
    资产(价值,包含(“a”、“b”));
    //或者,可以使用Asser4J、FEST Assert等。
    }};
    }
    
    我更喜欢使用
    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"))
                )
            )
        );
    

    谢谢你的来电+1,您也可以通过参数化
    asList
    (即
    数组。@JeffBowman很好。我会把它添加到答案中,希望你不介意。请这样做!我没有机会测试它,但你给出了一个非常好的答案,并给出了非常具体的解释,这应该得到应有的认可。过载不是原因;请看我对Jeff答案的评论。哇……我不会想到这个t、 我没有意识到编译器会因为重载问题而忽略这一点。我将运行一些快速测试,并找出哪个是正确的语句。Thx。结果表明,重载不是问题的原因。我们可以通过向测试类添加两个伪方法,并使用它们而不是Hamcrest匹配器来了解这一点:
    静态匹配器hasItems(T…items){return null;}
    静态匹配,这是一个很好的总结:不兼容的有界泛型是根本原因。请阅读OP的错误消息,您将了解为什么重载是症状中令人困惑的一部分。Mockito类似物是,对于它,泛型捕获可能更容易表示。请注意,您可能需要更加努力地忽略非匹配方法调用的方式与
    验证
    自动调用的方式相同。@JeffBowman JMockit的
    withCapture()
    相当于Mockito的
    ArgumentCaptor
    ,我相信它们都支持泛型类型。我不确定你所说的“更加努力地忽略非匹配方法调用”是什么意思但是,这两种API不也是一样的吗?问题是有一个Mockito标签,而不是JMockit,所以为了询问者的缘故,我提供了等效的标签。我认为两种捕获解决方案都是相同的,但与将匹配器传递到Mockito的解决方案相比,当有多个sam调用时,任何捕获解决方案都会变得复杂方法。然后您的捕获器将有多个值要检查,而不仅仅是一个值,并且您需要断言它们中的任何一个匹配。@JeffBowman问题的标题是“Mockito/JMockit”(它现在有“JMockit”标记)。当然,使用参数捕获不允许测试限制它将匹配的调用,而是“限制”Mockito和JMockit都是一样的。所以,“我的俘虏”和Mockito测试没有区别,这就是我要说的。
    <E> Matcher<Iterable<? extends E>> contains(E... items)
    
            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)));
    
    Mockito.verify(mock).doSomething(
       (Collection<String>) argThat(
            Matchers.contains(
                Arrays.<Matcher<? super String>> asList(
                    Matchers.equalTo("a"), Matchers.equalTo("b")
                )
            )
        )
    );
    
    @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.
        }};
    }
    
    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"))
                )
            )
        );