【问题标题】:Comparing two collections using hamcrest contains() method使用 hamcrest contains() 方法比较两个集合
【发布时间】:2025-11-29 14:15:02
【问题描述】:

我有两个集合,我试图在单元测试中比较它们是否相等,但我正在努力使用 contains 方法。这是我所拥有的:

@Test
public void getAllItems() {

    Collection<Item> actualItems = auction.getAllItems(joe);
    Collection<Item> expectedItems = Lists.newArrayList();
    expectedItems.add(iPhone);
    expectedItems.add(skateboard);
    assertThat(expectedItems, contains(actualItems));

}

items 包含与expectedItems 相同的对象,所以我希望断言是真的,但这是我得到的输出:

[Item{name=iPhone}, Item{name=Skateboard}]  --> Expected
[Item{name=iPhone}, Item{name=Skateboard}]  --> Actual

java.lang.AssertionError: 
Expected: iterable containing [<[Item{name=iPhone}, Item{name=Skateboard}]>]
     but: item 0: was <Item{name=iPhone}>
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8)

请您帮我解决使用contains 方法的问题所在?

public class Item {

    private String name;

    public Item(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return Objects.toStringHelper(this).add("name", name).toString();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Item other = (Item) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

}

【问题讨论】:

  • 可能有助于查看您的 Item 实施。
  • 你可能想在 Item 中定义一个 equals 方法。
  • 所以您希望您的项目列表包含项目列表?或者您是否希望您的项目列表包含项目?据我所知,contains 是一种可变参数方法,而不是基于CollectionassertThat(expectedItems, contains(items.toArray()));...
  • 你想多了。应该是assertThat(actualItems, is(equalTo(expectedItems))。来自@jimjarret 并回答类似的问题:*.com/a/46714949/1215301

标签: java hamcrest


【解决方案1】:

集合的.contains(...) 使用对象的equalshashCode 方法。为了在您自己的对象上使用equals(或在本例中为contains),您需要覆盖您的类的equalshashCode 方法。这是因为 Java 在后台使用引用,所以即使字段可能相等,对象引用也不相同。

在 Eclipse 中,您可以使用 right-mouse click -> Source -> Generate hashCode() and equals()... 生成它们。但是,由于您从未声明您使用 Eclipse,因此这里是生成的方法的示例:

// Overriding this class' equals and hashCode methods for Object comparing purposes 
// using the Collection's contains
// contains does the following behind the scenes: Check if both inputs aren't null, 
// check if the HashCodes match, check if the Objects are equal.
// Therefore to use the Collection's contains for Objects with the same fields, we
// need to override the Object's equals and hashCode methods
// These methods below are generated by Eclipse itself using "Source -> Generate 
// hashCode() and equals()..."
@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}
@Override
public boolean equals(Object obj) {
    if(this == obj)
        return true;
    if(obj == null)
        return false;
    if(getClass() != obj.getClass())
        return false;
    Item other = (Item) obj;
    if(name == null){
        if(other.name != null)
            return false;
    }
    else if(!name.equals(other.name))
        return false;
    return true;
}

如果您将这两个都添加到您的 Item 类中,contains 将起作用。


编辑:

我不确定,但是当我查看您的代码时,我认为以下可能是错误的:

@Test
public void getAllItems() {

    Collection<Item> actualItems = auction.getAllItems(joe);
    Collection<Item> expectedItems = Lists.newArrayList();

    // You first print both lists
    System.out.println(expectedItems);
    System.out.println(items);

    // And then add the two items to the expectedItems
    expectedItems.add(iPhone);
    expectedItems.add(skateboard);
    assertThat(expectedItems, contains(actualItems));
}

如果您尝试以下方法:

@Test
public void getAllItems() {

    Collection<Item> actualItems = auction.getAllItems(joe);
    Collection<Item> expectedItems = Lists.newArrayList();

    // First add both items
    expectedItems.add(iPhone);
    expectedItems.add(skateboard);

    // Then print both lists
    System.out.println(expectedItems);
    System.out.println(items);

    assertThat(expectedItems, contains(actualItems));
}

expectedList 现在是否包含 4 个项目?

[Item{name=iPhone}, Item{name=Skateboard}, Item{name=iPhone}, Item{name=Skateboard}]  --> Expected
[Item{name=iPhone}, Item{name=Skateboard}]  --> Actual

在这种情况下,您不应该添加这两个项目,因为它们已经存在于列表中。

另外,您正在尝试在整个列表中使用contains。通常contains 用于查看列表中是否存在单个项目。所以你可以使用这样的东西:

for(Item i : expectedList){
    assertTrue(actualList.contains(i));
}

or perhaps something like this, in case you use these libraries:

assertThat(actualList, is(expectedList));

我不确定这是否是原因以及是否可以解决它,因为您使用不同的 JUnit 库,然后我通常会这样做,我不确定这些带有 Asserts 的语法是否可行。

【讨论】:

  • 上面的方法我加了,还是不行。给我同样的信息。我已经用 Item 类的新定义更新了帖子。
  • @I.K.我对我的答案进行了编辑,但我不完全确定这是否能解决您的问题。
  • actualItems 包含一个 iphone 和一个滑板。对于 expectedItems,我手动插入 iPhone 和滑板,因为这是我所期望的。然后我做assertThat。我添加了系统输出语句以使帖子清晰,但我可能在那里失败了。所以每个集合中应该有两个项目。感谢您的尝试。
  • @I.K.啊k。无论如何,如果我没记错的话, contains 不适用于整个列表,因此您应该检查每个项目是否在列表中带有 for 循环,或者您应该使用类似 assertThat(actualList, is(expectedList)); 的东西来比较整个列表相互关联。
【解决方案2】:

我真的不认为你真的需要 hamcrest。通过以下方式之一进行断言会不会更容易:

列表在一天结束时仍然是一个对象:

org.junit.Assert.assertEquals(expected, actual)

使用containsAll(..) 的老式列表功能:

org.junit.Assert.assertTrue(expectedItems.containsAll(actualItems))

对数组的相等性使用断言:

org.junit.Assert.assertArrayEquals(expectedItems.toArray(), actualItems.toArray())

当然你也可以使用hamcrest

org.hamcrest.MatcherAssert.assertThat(actual, Matchers.containsInAnyOrder(actual.toArray()));

org.hamcrest.MatcherAssert.assertThat(actual, Matchers.contains(actual.toArray()));

【讨论】:

  • 我最后使用了containsAll。但我想知道如何使用 Hamcrest 库。它声称它可以比较两个集合,但我正在努力让它工作。
  • @I.K.我明白你的意思......所以我添加了一些额外的建议,包括 hamcrest 匹配器。他们会回答你的问题吗?
  • 谢谢。所以我想自然的问题是,当 junit 库可以处理比较集合时,hamcrest 有什么用。一般来说,hamcrest 提供了 junit 没有的哪些额外功能?
  • 正如我在最初的回复中提到的,对于这种使用 hamcrest 的断言并没有明显的好处。但是它的匹配器非常强大,你可以在他们的文档站点上找到它。例如,假设您想检查列表中的每个元素是否具有特定特征。顺便说一句,如果回复回答了您的问题,则标记为已回答也会对其他人有所帮助。谢谢!
【解决方案3】:

您基本上断言expectedItems 是一个包含一个元素的列表,而这个元素本身应该是一个包含iPhoneskateboard 两项的列表。

要断言expectedItemsactualItems 具有相同顺序的相同元素,请尝试以下操作:

@Test
public void getAllItems() {

    Collection<Item> actualItems = auction.getAllItems(joe);

    assertThat(actualItems, contains(iPhone, skateboard));
}

请注意assertThat 期望“实际”对象作为第一个参数,而不是“预期”。

您也可以这样做:

@Test
public void getAllItems() {

    Collection<Item> actualItems = auction.getAllItems(joe);
    Collection<Item> expectedItems = Lists.newArrayList();
    expectedItems.add(iPhone);
    expectedItems.add(skateboard);

    assertThat(actualItems, contains(expectedItems.toArray(new Item[expectedItems.size()])));
}

【讨论】: