使用hamcrest contains()方法比较两个集合

时间:2014-08-19 13:45:25

标签: java hamcrest

我有两个集合,我试图在单元测试中比较相等,但我正在努力使用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;
    }

}

3 个答案:

答案 0 :(得分:3)

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

在Eclipse中,您可以使用right-mouse click生成它们 - &gt; Source - &gt; 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。

答案 1 :(得分:3)

我真的不认为你真的需要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()));

答案 2 :(得分:0)

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

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

@Test
public void getAllItems() {

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

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

请注意assertThat期望&#34;实际&#34;对象作为第一个参数而不是&#34;期望&#34;。

或者你也可以这样做:

@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()])));
}