In a recent project, although some JUnit tests were available, but the readability of these tests was rather bad. In my view, there were two reasons that caused this poor readability: Firstly, the tests were far too long. The tests were neither short nor precise. Too many things have been attempted to test in a single test. Secondly, one has no assertion framework used as Hamcrest or AssertJ. It has been limited to the usual JUnit assertions, which has not contributed to the readability and finally maintainability of the tests.
Assertion frameworks have different objectives. Generally, they increase the readability of tests. Assertions of these frameworks are more expressive. This means more conditions can be checked in less code. Therefore tests are easier to understand. Moreover, these frameworks provide better error messages. In case of a test failure, it is not only said that the test is failed, but a precise error message is issued saying why the test was not successful. In addition, such frameworks provide a lot of flexiblilty. It is possible to create your own matcher allowing to write precise assertions for your own classes. Last but not least, these frameworks provide common patterns of assertions, which leads to an easy usage.
Hamcrest and AssertJ are both assertion frameworks that help to check conditions in tests. Both frameworks have the objective to make tests as readable as possible. Currently, Hamcrest is still more widespread and well-known, not least because this framework exists longer and is bundled with JUnit. However, AssertJ is evolving rapidly and is catching up with Hamcrest. Since Spring Boot’s version 1.4.0.M2 it is packed within the spring-boot-starter-test dependency.
Let me give you an example how to use these frameworks. Given is the String „Developer“ on which we want to perform several assertions. Let’s start how to do this with JUnit:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class JUnitStringAssert { | |
private static final String DEV = "Developer"; | |
@Test | |
public void testString() { | |
assertNotNull(DEV); | |
assertFalse("".equals(DEV)); | |
assertTrue(DEV.startsWith("Dev")); | |
assertTrue(DEV.contains("elo")); | |
assertEquals(9, DEV.length()); | |
} | |
} |
That is the way with JUnit most developers are familiar with. However, the assertions of JUnit are quite limited and mostly not really readable. This often means that you longer have to think about what is actually tested.
A better way offers Hamcrest whose assertions are more expressive:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class HamcrestStringAssert { | |
private static final String DEV = "Developer"; | |
@Test | |
public void testString() { | |
assertThat(DEV, is(notNullValue())); | |
assertThat(DEV, not(isEmptyString())); | |
assertThat(DEV, startsWith("Dev")); | |
assertThat(DEV.length(), equalTo(9)); | |
} | |
} |
Hamcrest provides more powerful assertions and make them easier to read and understand. However, personally I didn’t find it that easy to get started with Hamcrest. Note that in Hamcrest, the expected and actual values are reversed compared to JUnit. While JUnit expects the expected value always as the first argument, Hamcrest expects it as the second argument.
An even better alternative and in my opinion the most elegant and easiest way to perform assertions provides AssertJ:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class AssertJStringAssert { | |
private static final String DEV = "Developer"; | |
@Test | |
public void testStrings_WithAssertJ() { | |
assertThat(DEV).isNotNull().isNotEmpty().startsWith("Dev").contains("elo").hasSize(9); | |
} | |
} |
The assertions of AssertJ are even more readable and are the most extensive. The framework provides a fluent api which makes it really simple and intuitive to use. Assertions can be read from left to right like a normal sentence. Furhermore, AssertJ provides the most meaningful failure messages, is actively developed and community driven, has strong Java 8 support and lots of examples makes it simple to get started.