November 23, 2014
Hot Topics:

Write More Understandable Java Tests with Matcher Objects and FEST-Assert

  • August 30, 2010
  • By Shekhar Gulati
  • Send Email »
  • More Articles »

When writing Java unit tests, developers often face the problem of their unit test assertions being long and hard to read. For example, consider the following JUnit test:


private List words;
@Before
public void setUp() {
words = Arrays.asList("available", "avenge", "avenue", "average");
}

@Test
public void shouldContainAllElementsStartingWithavAndEndingWithe() {
for (String word : words) {
assertTrue(word.startsWith("av") && word.endsWith("e"));
}
}

This JUnit test case has a setUp method (annotated with the @Before annotation) that will initialize some data for the test, and then it has a test method. To test that all the elements in the collection start with av and end with e, the code first iterates over the loop and then uses two assertions (starting with av and ending with e). This test method also includes some boilerplate code, which iterates over a loop and then fires assertions on each element.

This code is rather hard to read and understand, isn't it? Wouldn't it be great if you could do all this testing in one line of code? In this article, I will demonstrate how to use a library of matchers for building test expressions. The library is called Hamcrest and it contains lots of helpful matcher objects. It helps you specify matching rules which you can use for unit testing.

 

Benefits of Using Matchers in Java Tests

Let’s rewrite the above JUnit test using Hamcrest library.


@Test
public void shouldContainAllElementsStartingWithavAndEndingWithe() {
assertThat(words, everyItem(both(startsWith("av")).and(endsWith("e"))));
}

In this test method, we use the assertThat method with the Hamcrest matchers nested within each other. Nesting is one of Hamcrest’s powerful features because it allows you to build complex assertions. The assertThat method has the following signature:


public static void assertThat(T actual, Matcher matcher)

It asserts that actual satisfies the condition specified by matcher and will throw an AssertionError if the actual does not satisfy the condition.

As you can see, the test case written using Hamcrest is much more readable as it communicates its intention very clearly. It can be read as follows: assert that every word in the words collection should start with "av" and end with "e. " As you start using matchers, you will learn how easy it is to write assertions that are human readable and easy to understand. They will just become a part of your unit testing toolkit.

The two main benefits of using matchers in your unit tests are:

  1. Clean and readable code: If you have followed the discussion you understand that with the help of matchers your test code can be as easy to read as simple English.
  2. Descriptive failure message: If the test fails, the failure trace is very descriptive, which helps in debugging the problem.

Let’s modify the previous example by adding another word "aver" in our list and then rerun the test. Now, both test cases will fail but they will emit different failure traces.

Without Hamcrest Matchers


java.lang.AssertionError:
at org.junit.Assert.fail(Assert.java:91)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at com.shekhar.junit.JUnitMatchersTest.shouldContainAllElementsStartingWithavAndEndingWithe(JUnitMatchersTest.java:43)

Using Hamcrest Matchers


java.lang.AssertionError:
Expected: each (a string starting with "av" and a string ending with "e")
got: <[available, avenge, avenue, average, avert]>

at org.junit.Assert.assertThat(Assert.java:778)
at org.junit.Assert.assertThat(Assert.java:736)
at com.shekhar.junit.JUnitMatchersTest.shouldContainAllElementsStartingWithavAndEndingWithe(JUnitMatchersTest.java:50)

As you can see, the failure trace emitted by Hamcrest is much more descriptive and tells you where the problem is. Any developer can very easily debug the problem using this failure information.

Now I will talk about why you should use matchers in your code. Let’s take a look at matcher support in JUnit.


Tags: Java, testing



Page 1 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel