Introduction to Hamcrest Collection Matchers

Another post in the series of Hamcrest Matchers, a framework for writing test matcher objects. This week we’ll dive into collections and iterables, and we’ll see how we can use Hamcrest for making those asserts even more readable.

In many software projects I encounter code like the following:

assertEquals("Collection size doesn't match", 4, reds.size());

Or, even worse:

assertTrue(greens.size() == 0);

The second loses context by not knowning the expected value and the actual value; it knows only that they are not equal.

So, while this above might be a valid asserts, their readability and error feedback can be improved by using a framework like Hamcrest. This can be done in the following way:

assertThat(reds, hasSize(4));
assertThat(greens, is(empty()));

In my opinion, this increases readability quite a bit, and makes it easier to maintain tests like this. Check the following piece of code for more examples:


    @Test
    public void testIterablesMatchers() {
        Catalogue catalogue = new Catalogue();

        catalogue.addMovie(new Movie("Lord of the Rings"));
        catalogue.addMovie(new Movie("The Ring"));
        catalogue.addMovie(new Movie("The Bling Ring"));

        List movies = catalogue.getMovies();

        // Check each item for a certain condition
        assertThat(movies, everyItem(Matchers.hasProperty("title", containsString("Ring"))));

        // Checks that the collection has at least one item which matches the specified matcher.
        assertThat(movies, hasItem(new Movie("The Ring")));
        // Checks that the collection has at least one or multiple items which match the specified matcher.
        assertThat(movies, hasItems(new Movie("The Ring"), new Movie("Lord of the Rings")));

        // Creates a matcher matching examined iterables that yield no items.
        assertThat(new ArrayList(), emptyIterable());

        // Checks that all of the items match the expected items, in the same order.
        assertThat(movies, contains(new Movie("Lord of the Rings"), new Movie("The Ring"), new Movie("The Bling Ring")));
        // Checks that all of the items match the expected items, in any order.
        assertThat(movies, containsInAnyOrder(new Movie("The Ring"), new Movie("The Bling Ring"), new Movie("Lord of the Rings")));

        // Beware here: iterableWithSize is a bit funky with Generics, so the bast (and only) way to solve this is to be explicit about the generics
        assertThat(Arrays.asList("foo", "bar"), IsIterableWithSize.iterableWithSize(2));

        // Matches when the size() method returns a value equal to the specified size.
        assertThat(movies, hasSize(3));

        // Checks that the examined collections whose isEmpty method returns true.
        assertThat(new ArrayList(), is(empty()));
        // Checks that the examined collections whose isEmpty method returns true.
        assertThat(new ArrayList(), is(emptyCollectionOf(Movie.class)));
    }
Error messages

Whenever a assertion fails, you get a pretty clear error message, which will help you quickly solve issues you run into. For example, if in the above code example I would have made a wrong assumption, and instead of:

        assertThat(movies, hasSize(3));

If would have typed:

        assertThat(movies, hasSize(2));

The following error message would have shown up:

java.lang.AssertionError:
Expected: a collection with size 
     but: collection size was 

Whereas the the JUnit equivalent:

assertEquals(2, movies.size());

Would have resulted in the following:

junit.framework.AssertionFailedError:
Expected :2
Actual   :3

Which would have helped a great deal also, but it’s just that the Hamcrest one is a bit more clear, and a bit more elaborate on the error message.

I hope this helps in picking up Hamcrest and that it helps in writing clear, consistent tests. See you next week!

4 Comments

  1. Hey! Heads up, you have to Override equals for any of this to work. Also heads up. If you want good errors on failed assertions, you should override toString.

    • Hello Alex,

      You’re completely right! I had it in my source code, but I forgot to mention it on the blog. Thanks for mentioning it!

      Erik

  2. Stephen

    Is there one that compares all elements of an array or collection to another, even for ones with guaranteed ordering?