Сравнение двух коллекций с помощью метода hamcrest contains()

у меня есть две коллекции, которые я пытаюсь сравнить для равенства в моих модульных тестах, но я борюсь с методом 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 ответов


коллекции .contains(...) использует equals и hashCode методы объектов. Для того, чтобы использовать equals (или в данном случае contains) на ваших собственных объектах вам нужно переопределить equals и hashCode методы класса. Это связано с тем, что 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 будет работать.


EDIT:

я не уверен, но когда я смотрю на ваш код, я думаю, следующим может быть не так:

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

или, возможно, что-то вроде этого, если вы используете эти библиотеки:

assertThat(actualList, is(expectedList));

я не уверен, что это причина, и если это исправит это, так как вы используете другой JUnit библиотека тогда я обычно делаю, и я не уверен, что этот синтаксис с утверждениями возможен.


Я действительно не думаю, что вам действительно нужен hamcrest для этого. Не проще ли было бы сделать утверждения одним из следующих способов:

список по-прежнему является объектом в конце дня:

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

старая функциональность моды для списков с помощью containsAll(..):

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

использование утверждает равенство массивов:

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

конечно, вы можете использовать hamcrest as хорошо:

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

или

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

вы в основном утверждаете, что expectedItems - список из одного элемента и этот элемент, как ожидается, будет сам список с двумя элементами iPhone и skateboard.

утверждать, что expectedItems и actualItems имеют одни и те же элементы в том же порядке, попробуйте это:

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