Assert Collections Fluently

comicbooks_umag_15copy-2
The one thing I love the most about Java is the variety of libraries and frameworks available. A couple of weeks ago I was writing a lot of code involving Collections and was irritated about the bloated unit tests associated with it. When I could not stand it anymore, I started googling and stumbled upon a nice little library called FEST assertions and it totally made my day.

Assert Collections The Ugly Way

Suppose you have a HeroRepository to store and retrieve all heroes from comic books and you want to retrieve the names of the heroes published by a certain publisher. When you only use JUnit a typical test involves retrieving the collection of hero names and assert that the collection is not empty, has the expected size and contains all the names you expect (see the code block below).

@Test
public void findHeroNameByPublisher() {
    HeroRepository heroRepository = new InMemoryHeroRepository();
    List heroes = heroRepository.findHeroNameByPublisher("Marvel");
    assertNotNull(heroes);
    assertEquals(3, heroes.size());
    assertTrue(heroes.containsAll(Arrays.asList("Spider-Man", "X-Men", 
        "Fantastic Four")));
}

Not that hard to read, but already not as clean as you would like the test to be. In my opinion an ideal test should only contain 3 lines of code:
• 1 line to setup the test (arrange),
• 1 line to call the method under test (act),
• 1 line to check the outcome (assert)
This test breaks with this rule. You could (and should) fix this by wrapping the 3 asserts in a single private method assertThatReturnedHeroNamesAre(). That way it is easier to figure out what the code tests.
This was the simplest thing I could think of with Collections and already I needed 3 assertions to test the outcome. It becomes uglier and uglier when you do more complex stuff. Let’s for example take a look at what happens with our test code if the method under test returns a collection of Hero instead of String. See the code below. I replaced the assertions by the suggested private method assertThatReturnedHeroNamesAre. If we look into that method, you can see that we have an extra for loop to test whether the names of our heroes are the names we expect. Yak!

@Test
public void findHeroesByPublisher() {
    HeroRepository heroRepository = new InMemoryHeroRepository();
    List heroes = heroRepository.findHeroesByPublisher("Marvel");
    assertThatReturnedHeroNamesAre(Arrays.asList("Spider-Man", "X-Men", 
        "Fantastic Four"), heroes);
}

private void assertThatReturnedHeroNamesAre(List expectedHeroNames, 
            List actualHeroes) {
    assertNotNull(actualHeroes);
    assertEquals(expectedHeroNames.size(), actualHeroes.size());
    for (Hero hero : actualHeroes) {
        assertTrue(expectedHeroNames.contains(hero.getName()));
    }
}

Assert Collections The Fluent Way

Fortunately the library FEST assertions comes to the rescue. Take a look at how the first example is handled using fluent assertions. As you can see, this almost reads like an English book.

@Test
public void findHeroNameByPublisher() {
    HeroRepository heroRepository = new InMemoryHeroRepository();
    List heroes = heroRepository.findHeroNameByPublisher("Marvel");
    assertThat(heroes).containsOnly("X-Men", "Fantastic Four", "Spider-Man");
}

Nice, huh? We don’t need to assert that the collection is not null and that the size is equal, because this is implicitly handled by the containsOnly assertion. So, let’s rewrite the second example by replacing the JUnit code with the correct fluent assertion. How would we describe the test in plain English: assert that the names of the heroes returned by the method should only contain “Spider-Man”, “X-Men” and “Fantastic Four”. Let us now look at the code. It almost looks the same. We use the static method extractProperty to get a specific property of the objects in the collection. In this example we used it on a direct property of Hero, but it can also be used on nested properties (i.e. address.street).

@Test
public void findHeroesByPublisher() {
    HeroRepository heroRepository = new InMemoryHeroRepository();
    List heroes = heroRepository.findHeroesByPublisher("Marvel");
    assertThat(extractProperty("name").from(heroes))
        .containsOnly("Spider-Man", "X-Men", "Fantastic Four");
}

Not Only For Collections

FEST assertions is a Java library that provides a fluent interface for writing assertions. Its main goal is to improve test code readability and make maintenance of tests easier. Fest provides assertions for the following data types:
• Object
• String
• Date
• Primitives (boolean, int, char, etc.)
• BigDecimal
• Iterable
• Arrays of Object
• Arrays of primitives
• Map
• Throwable
• File and InputStream
• BufferedImage

This is one library I will definitely keep in my suitcase. Another library for writing fluent assertions is Hamcrest. In another blogpost I will explain the differences between the two libraries.
You can download the sources I used for the example at the end of this article.