Since defining equals()
and hashCode()
only for testing purpose is considered as a code smell, I prefer to use ReflectionEquals
or custom matchers to compare objects while doing unit testing.
However, I don't know how to use ReflectionEquals
or custom matchers in comparing lists of user-defined classes.
For example, how do I assert the following code without defining equals()
and hashCode()
(maybe only use ReflectionEquals
or custom matchers)?
// When
List<Record> actual = sut.findBySomeId();
// Then
List<Record> expected = asList(
aRecord()...build(),
aRecord()...build()
);
assertThat(expected, /* how to compare? */);
There is a super fluent library for solving your issue, called AssertJ. It is quite easy to use, flexible to make changes and its fluency makes the tests easy to read! In order to use it, first you have to import the right package
import static org.assertj.core.api.Assertions.*;
I do not know how your Record
model looks like, so I made a dummy one:
public class Record {
private String name;
private String data;
private String history;
public Record(String name, String data, String history) {
this.name = name;
this.data = data;
this.history = history;
}
public String getName() {
return name;
}
public String getData() {
return data;
}
public String getHistory() {
return history;
}
}
Then the test would like like, if you only want to assert list of Record
based on one field:
@Test
public void give_when_then() throws Exception {
List<Record> actualList = sut.findBySomeId();
assertThat(actualList)
.extracting(record -> record.getData())
.containsExactly("data1", "data2", "data3");
}
If you want to assert the objects based on multiple fields, you can do for example:
@Test
public void give_when_then() throws Exception {
List<Record> actualList = sut.findBySomeId();
assertThat(actualList)
.extracting(
record -> record.getName(),
record -> record.getHistory())
.containsExactly(
tuple("name1", "history1"),
tuple("name2", "history2"),
tuple("name3", "history3"));
}
...where tuple is also an assert4J object for wrapping the results.
Furthermore there are bunch of assert methods like containsExactlyInAnyOrder(...)
, containsAll(...)
, containsAnyOf(...)
etc., which could also help your life in certain cases.
Last but not least you can also write your own specific object assert class extending AbstractObjectAssert
base class. With this you will have (among others) a method called isEqualToComparingFieldByField
. From the official documentation:
Assert that actual object is equal to the given object based on a property/field by property/field comparison (including inherited ones). This can be handy if equals implementation of objects to compare does not suit you.
First you define your own assert class:
public class RecordAssert extends AbstractObjectAssert<RecordAssert, Record> {
public RecordAssert(Record actual) {
super(actual, RecordAssert.class);
}
}
Then the usage of it would look like:
@Test
public void give_when_then() throws Exception {
List<Record> actual = sut.findBySomeId();
assertThat(actual.get(0)).isEqualToComparingFieldByField(new Record("name1", "data1", "history1"));
// Asserts for other objects
}
I hope it helps!
The Hamcrest library has a great selection of matchers for making assertions on collections types. In particular, the hasItem
, hasItems
, contains
and containsAnyOrder
matchers as these can use matchers themselves (I like to use TypeSafeMatcher) to test the items in the collections.
I'll leave you to decide which one best suits your needs, but I'll use contains
for my example:
List<Record> actual = sut.findBySomeId();
Record expected1 = aRecord()...build();
Record expected2 = aRecord()...build();
assertThat(actual, contains(matchingRecord(expected1), matchingRecord(expected2));
...
// somewhere the test has access to it
private Matcher<Record> matchingRecord(Record expected) {
return new TypeSafeMatcher<Record>() {
public boolean matchesSafely(Record actual) {
// perform tests and return result, e.g.
return actual.getValue() == expected.getValue();
}
public void describeMismatchSafely(Record record, Description mismatchDescription) {
mismatchDescription.appendText("give a meaningful message");
}
};
}
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