In this series on unit testing with JUnit, we started with JUnit tests both using Maven and IntelliJ in the first part. In the second part, we learned about assertions, JUnit 4 annotations, and test suites. In this post, we will cover assertThat, a more expressive style of assertion that uses Hamcrest matchers.
Assertions with assertThat
The classic JUnit assertions, such as assertEquals, assertTrue, and so on are simple to understand and use. But, by using assertThat with Hamcrest matchers, It’s easy to make dramatic improvements to your tests. Hamcrest is a framework that provides support for Java unit testing. Hamcrest contains self-contained classes, called matchers with static methods designed to be used with JUnit assertThat.
What you can do with the classic assertions, you can also do with assertThat, but more fluently, and make tests more readable. For example, take a look at the following assertions:
. . . assertFalse(expected.equals(actual)); assertThat(actual, is(not(equalTo(expected)))); . . .
As it is relevant, the second assertion is more readable. If you read out, the second assertion reads more like a sentence – “Assert that actual is not equal to expected”.
Beside test readability, readability of test failures is another highlight of assertThat, as shown in the following figure.
As you can see, the second assertion failure message of assertThat is far more explanatory as compared to assertFalse. It was because we used a core Hamcrest matcher.
When you write an assertThat method, you pass two parameters to it. The first is the actual result, typically the value/object returned by the method under test. The second parameter is a matcher obtained from a call to a matcher method. The matcher is an object that matches the test rule. To see how it works, we will write a class with few methods that we will test.
In the class above, we wrote a
toConcatedUpperCase() method that concatenates two strings passed as parameters, converts the result to uppercase, and returns it. We then wrote a
floatingPointMultiplication() method that returns the product of two
double values passed to it. We also wrote the
getStringCollection() that adds a string to a
Set collection and returns the
We will next write a test class with few test methods.
In the test class above, we started with a static import of the Hamcrest core matcher, in Line 6 . We then used the
@Before annotation to instantiate the
MatchersDemo class. Recall from the previous post that the
@Before annotated method will run before each
@Test method in the class. Then, we wrote the assertions:
- Line 18: We wrote an
ismethod containing the
equalTomethod. We can read it as-“assert that actual (the value that toConcatedUpperCase() method returns) is equal to expected (HELLOWORLD). Although not necessary, many programmers like to use
isand other matcher methods together because it makes assertions more readable. This is the very reason for the existence of
isand being referred as decorator: to decorate other matchers.
- Line 24: We used
startsWithto assert that the actual string starts with the expected value,
- Line 31: We used
containsStringto test that the actual string contains the expected value
- Line 37: We wrote an
allOf, and things get interesting here.
allOftakes multiple matcher methods and returns a matcher. This matcher test if the actual result matches all the specified matchers- think about the Java short circuit && operator. So, in a single assertion, we asserted that the actual result is not a null value, is an instance of the String class, and starts with and contains HELLO. The corresponding matcher method, that works like the Java short circuit || operator is anyOf.
When we run the test class above, all the test passes. But, the core matchers we used are only a subset of the wide range of Hamcrest matchers. There are additional matchers for specific test requirements, such as testing collections, numbers, text comparison, and so on. The aditional matchers are not part of JUnit, and to use them, we need to seperately download the Hamcrest matcher library and point the project classpath to it. If you are using Maven, add the following dependency to the pom.xml file.
. . . <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>1.3</version> <scope>test</scope> </dependency> . . .
Collections often have more complex testing needs. For example testing the size of a collection, testing for one or more elements in a collection, their ordering, etc. The Hamcrest collection matchers are designed to support the needs of testing collections in unit tests.
Let’s write a new test class and use the Hamcrest collection matchers.
In the test class above, we initialized the
Set collection of
MatchersDemo with few strings in a
@Before method. Then, we wrote the following assertions:
- Line 23: We wrote a
hasSizeto test the size of the collection.
- Line 32: We used
hasItemsto test for multiple items in the collection. To test for a single item, you can use
- Line 41: We used
containsInAnyOrderto test that all items in the collection match the expected items, in any order. If you want to test for all items in the same order, use the stricter
I particularly find the Hamcrest number matchers useful to test floating point calculations that provide accurate approximations, but not exact results. The assertion
assertThat(2.32 * 3, equalTo(6.96)); will fail because the actual result is not what we expect (6.96). By, looking at the failure message we will understand the reason.
java.lang.AssertionError: Expected: <6.96> but: was <6.959999999999999> . . . .
As you can notice, the actual value is different from what we expected. To test for such floating point calculations, there is a
closeTo matcher method that we will cover now.
In Line 20 of the test class above, we used
closeTo to test for the result of the
floatingPointMultiplication() method under test. The
closeTo method matches if an examined double value is equal to the first parameter value, within a range of +/- error specified by the second parameter. We also wrote an
greaterThan in Line 26 to check if the actual value returned by the method under test is greater than the specified value
Some other number matcher methods are
lessThanOrEqualTo. As their names are self-explanatory, I won’t explain them further.
Text Comparison Matchers
We did some text comparisons with the core matchers on the
toConcatedUpperCase() method earlier in this post. But, for more flexibility, let’s look at some specific text comparison matchers.
In Line 21 and Line 27 we used
equalToIgnoringWhiteSpace to test for string equality while ignoring casing and white spaces respectively. In Line 33 we used
stringContainsInOrder to test that the actual result contains the specified strings in the same sequence.
The Hamcrest matchers library is big. In this post we looked at few of them. But more importantly, we learned how unit tests are done the real way – Hamcrest matchers.
As you saw, JUnit’s assertThat combined with Hamcrest matchers has much better functionality. But by saying so, the old assert methods are here to stay. If you are running the old assert methods present in existing test code, you may continue doing so. But, if you plan to write new test code, consider using the Hamcrest matchers. They are more readable – as the JUnit release note says “This syntax allows you to think in terms of subject, verb, object– assert that x is 3”. Also, you will realize the full benefits when your test fails during complex testing. The detailed failure message will point you in the right direction at a very less, or no time at all.
During unit testing enterprise application code using the Spring Framework, you can unleash the potentials of the Hamcrest matchers. Beside the regular assertions, you can use the Hamcrest bean matchers to test properties of Spring beans. You can also use matchers to test whether a view name contains a specific string in Spring MVC, test responses from mock objects. You’ll find the versatility of the Hamcrest matchers very beneficial when writing unit tests in JUnit.