I'm just wondering how folks unit test and assert that the "expected" collection is the same/similar as the "actual" collection (order is not important).
To perform this assertion, I wrote my simple assert API:-
public void assertCollection(Collection<?> expectedCollection, Collection<?> actualCollection) { assertNotNull(expectedCollection); assertNotNull(actualCollection); assertEquals(expectedCollection.size(), actualCollection.size()); assertTrue(expectedCollection.containsAll(actualCollection)); assertTrue(actualCollection.containsAll(expectedCollection)); }
Well, it works. It's pretty simple if I'm asserting just bunch of Integers or Strings. It can also be pretty painful if I'm trying to assert a collection of Hibernate domains, say for example. The collection.containsAll(..) relies on the equals(..) to perform the check, but I always override the equals(..) in my Hibernate domains to check only the business keys (which is the best practice stated in the Hibernate website) and not all the fields of that domain. Sure, it makes sense to check just against the business keys, but there are times I really want to make sure all the fields are correct, not just the business keys (for example, new data entry record). So, in this case, I can't mess around with the domain.equals(..) and it almost seems like I need to implement some comparators for just unit testing purposes instead of relying on collection.containsAll(..).
Are there some testing libraries I could leverage here? How do you test your collection?
Thanks.
I'm not sure what version of JUnit you're using, but recent ones have an assertThat
method which takes a Hamcrest Matcher as an argument. They're composable so you can build up complex assertions about a collection.
For instance, if you wanted to assert that a collection A
contained every element in collection B
, you could write:
import static org.junit.Assert.*; import static org.junit.matchers.JUnitMatchers.*; import static org.hamcrest.core.IsCollectionContaining.*; import static org.hamcrest.collection.IsCollectionWithSize.*; import org.hamcrest.beans.SamePropertyValuesAs; public class CollectionTests { /* * Tests that a contains every element in b (using the equals() * method of each element) and that a has the same size as b. */ @Test public void test() { Collection<Foo> a = doSomething(); Collection<Foo> b = expectedAnswer; assertThat(a, both(hasItems(b)).and(hasSize(b.size()))); } /* * Tests that a contains every element in b (using introspection * to compare bean properties) and that a has the same size as b. */ @Test public void testBeans() { Collection<Foo> a = doSomething(); Collection<Foo> b = expectedAnswer; Collection<Matcher<Foo>> bBeanMatchers = new LinkedList<Matcher<Foo>>(); // create a matcher that checks for the property values of each Foo for(Foo foo: B) bBeanMatchers.add(new SamePropertyValuesAs(foo)); assertThat(a, both(hasItems(bBeanMatchers)).and(hasSize(b.size()))) } }
The first test just uses the equalTo() matcher on every object (which will delegate to your equals implementation). If that's not strong enough, you can use the second case, which will use getters and setters to compare every element. Finally, you can even write your own matchers. The Hamcrest package doesn't come with a matcher for matching by field (as opposed to matching bean properties), but it's trivial to write a FieldMatcher (and indeed is a good exercise).
The Matchers are a bit odd at first, but if you follow their example of making new Matchers have a static method that returns the matcher you can do a bunch of import static
s and your code basically reads like an English sentence ("assert that a both has the items in b and has the same size as b"). You can build up a pretty impressive DSL with these things and make your test code a lot more elegant.
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