I have two collections which I am trying to compare for equality in my unit tests, but I am struggling with the contains method. Here is what I have:
@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
contains the same objects as expectedItems
so I would expect the assertion to be true but this is the output I get:
[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)
Please can you help me where I am going wrong with using the contains
method?
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;
}
}
A Collection's .contains(...)
uses the equals
and hashCode
methods of the Objects. In order to use equals
(or in this case contains
) on your own Objects, you need to override the equals
and hashCode
methods of your class. This is because Java uses references behind the scenes, so even though the field may be equal, the Object-references are not.
In Eclipse you can generate them using right-mouse click
-> Source
-> Generate hashCode() and equals()...
. But, since you never stated you use Eclipse, here is an example of the methods that are generated:
// 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;
}
If you add both of these to your Item
-class, the contains
will work.
EDIT:
I'm not sure, but when I look at your code I think the following might be wrong:
@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));
}
If you try the following instead:
@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));
}
Does the expectedList now contain 4 items?
[Item{name=iPhone}, Item{name=Skateboard}, Item{name=iPhone}, Item{name=Skateboard}] --> Expected
[Item{name=iPhone}, Item{name=Skateboard}] --> Actual
In that case you shouldn't add the two items, since they are already present in the list.
Also, you're trying to use the contains
on the entire list. Normally the contains
is used to see if a single item is present in the list. So you could either use something like this:
for(Item i : expectedList){
assertTrue(actualList.contains(i));
}
or perhaps something like this, in case you use these libraries:
assertThat(actualList, is(expectedList));
I'm not sure if this is the cause and if this will fix it, since you use a different JUnit library then I usually do and I'm not sure if these syntax with the Asserts are possible.
I really don't think you actually need hamcrest for this. Wouldn't it be easier to make the asserts in one of the following ways:
A list is still an object at the end of the day:
org.junit.Assert.assertEquals(expected, actual)
An old fashion functionality for lists by using containsAll(..):
org.junit.Assert.assertTrue(expectedItems.containsAll(actualItems))
Using asserts for arrays' equality:
org.junit.Assert.assertArrayEquals(expectedItems.toArray(), actualItems.toArray())
Of course you can use hamcrest as well:
org.hamcrest.MatcherAssert.assertThat(actual, Matchers.containsInAnyOrder(actual.toArray()));
OR
org.hamcrest.MatcherAssert.assertThat(actual, Matchers.contains(actual.toArray()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With