hamcrest - 比较集合中的项目

hamcrest - compare items in the collection

任何人都可以向我解释 hamcrest 如何比较集合以及库中的不同方法有什么用?
我想了解 IsIterableContainingInAnyOrder#containsInAnyOrder 方法是如何工作的。

IsIterableContainingInAnyOrderclass:

中有3个重载方法

我的测试用例:

import org.hamcrest.Matchers;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.assertThat;

public class HamcrestCollections {

    @Test
    public void myTest(){

        List<String> expected = Arrays.asList("one","two","four");

        List<String> actual = Arrays.asList("two","one");

        // This doesn't compile
        assertThat(actual, Matchers.containsInAnyOrder(expected));

        assertThat(actual, Matchers.containsInAnyOrder(expected.toArray()));
    }
}

第一个断言没有编译,错误是:

Error:(19, 9) java: no suitable method found for assertThat(java.util.List<java.lang.String>,org.hamcrest.Matcher<java.lang.Iterable<? extends java.util.List<java.lang.String>>>)
    method org.junit.Assert.<T>assertThat(java.lang.String,T,org.hamcrest.Matcher<? super T>) is not applicable
      (cannot infer type-variable(s) T
        (actual and formal argument lists differ in length))
    method org.junit.Assert.<T>assertThat(T,org.hamcrest.Matcher<? super T>) is not applicable
      (inferred type does not conform to upper bound(s)
        inferred: java.util.List<java.lang.String>
        upper bound(s): java.lang.Iterable<? extends java.util.List<java.lang.String>>,java.lang.Object)

我真的不明白这条消息是怎么回事。


我发现它必须转换为数组才能工作(示例中的第二个断言):

Matchers.containsInAnyOrder(expected.toArray()));

我想在这种情况下使用了库中的这个方法:containsInAnyOrder(T... items),是真的吗?

但是如何使用 IsIterableContainingInAnyOrder 中剩余的方法呢?有什么方法可以比较集合而不将它们转换为数组吗?

您的代码在 JDK 1.8.0_12、Hamcrest 1.3 和 JUnit 4.12 的第一种形式中编译得很好,尽管由于 它没有产生预期的结果gotcha我会在下面解释

我只能猜测您可能有多种库版本,或 jdk,或类似的东西。但是,我认为这并不重要,因为我提到了 gotcha


Can anyone explain to me how hamcrest compares collections and and what the different methods from the library are for?

简而言之,您可以提供自己的 array/collection 个匹配器,或者提供它将为其创建匹配器的项目数组。之后,根据生成的匹配器列表验证实际项目。

如果您检查 the sources,您会看到 IsIterableContainingInAnyOrder 构造函数接受一组匹配器:

public IsIterableContainingInAnyOrder(Collection<Matcher<? super T>> matchers) {
    this.matchers = matchers;
}

...虽然您想知道的方法是 工厂方法 ,其中 return 一个 IsIterableContainingInAnyOrder 实例。一个已弃用,我已跳过它。然后我们有以下 2,其中第一个委托给第二个,没有任何花哨的事情发生:

public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(Matcher<? super T>... itemMatchers) {
    return containsInAnyOrder(Arrays.asList(itemMatchers));
}

public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(Collection<Matcher<? super T>> itemMatchers) {
    return new IsIterableContainingInAnyOrder<T>(itemMatchers);
}

...最后我们有:

public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(T... items) {
    List<Matcher<? super T>> matchers = new ArrayList<Matcher<? super T>>();
    for (T item : items) {
        matchers.add(equalTo(item));
    }

    return new IsIterableContainingInAnyOrder<T>(matchers);
}

如您所见,为每个项目创建了一个匹配器,有点像 gotcha:

  • 如果您传递一个参数数组,您将获得每个项目的匹配器

assertThat(actual, containsInAnyOrder("one", "two", "four")); 产生:

java.lang.AssertionError: 
Expected: iterable over ["one", "two", "four"] in any order
     but: No item matches: "four" in ["two", "one"]
  • 如果你传递一个列表,它会算作一个 1 个参数数组,并且只会为列表本身创建 1 个匹配器

assertThat(actual, containsInAnyOrder(Arrays.asList("one", "two", "four"))) 产生:

java.lang.AssertionError: 
Expected: iterable over [<[one, two, four]>] in any order
     but: Not matched: "two"

注意到细微差别了吗?


I found that it must be converted to the array in order to work (the second assertion in the example):

Matchers.containsInAnyOrder(expected.toArray())); I suppose that in this case this method from the library is used:containsInAnyOrder(T... items), is that true ?

But how remaining methods from IsIterableContainingInAnyOrder can be used ? Is there any way to compare collections without converting them to the array?

只需按原样使用 内嵌形式assertThat(myList, containsInAnyOrder("one", "two", "four"))。我想这提供了更好的可读性并避免了不必要的变量或多余的编码,例如 expected 集合。通常您需要检查几项,而不是数百项,对吗?