Unit Test with Hamcrest in java
assertThat() with Hamcrest Matchers
In this tutorial, we’ll learn how to write efficient Junit test cases in Java using Hamcrest assertThat()
with Matchers.
Hamcrest
Hamcrest is a widely used framework for writing unit test cases in Java. Sometimes it becomes difficult to write Junit test cases to test complex conditions, Hamcrest comes in handy in such cases, which provides pre-defined Matchers to specify conditions for Texts (String), Numbers (Integer, Long, Double, and Float), Collections (List, Array, and Map), Objects and many more.
You write unit tests with Hamcrest using assertThat()
statement followed by one or more, or nested Matchers. We will look at all of Matcher’s examples in this article. Let’s do the setup first.
Setup Hamcrest
Import the required Hamcrest dependency for Java using Maven (pom.xml) or Gradle (build.gradle), whichever you are using:-
<!-- add dependency in pom.xml -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
// add dependency in build.gradle
dependencies {
testImplementation 'org.hamcrest:hamcrest:2.2'
}
Import Hamcrest
Once you add the Hamcrest dependency, you can import static Hamcrest assertThat()
method and All Matchers in unit test class like this:-
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
We are ready to write unit test cases with Hamcrest.
Using Hamcrest Matchers
Matcher to check null value
These are quite often used Hamcrest Object Matchers to check the null and non-null value:-
- Assert that a given value is Null:-
@Test public void givenValue_whenNull_thenPass() { String text = null; Integer number = null; Boolean flag = null; Object obj = null; // assertThat(null, is(null)); <- throw NullPointerException assertThat(null, is(nullValue())); // pass assertThat(text, is(nullValue())); // pass assertThat(number, is(nullValue())); // pass assertThat(flag, is(nullValue())); // pass assertThat(obj, is(nullValue())); // pass }
- Assert that a given value is Not Null:-
@Test public void givenValue_whenNotNull_thenPass() { assertThat("a", is(notNullValue())); // pass assertThat(1, is(notNullValue())); // pass assertThat(false, is(notNullValue())); // pass assertThat(new Object(), is(notNullValue())); // pass }
Text Matchers for String
Hamcrest Text Matchers are used to write easier and neater assertions on String with various conditions. Let’s look at them:-
-
Assert that a String is empty:-
@Test public void givenString_whenEmpty_thenPass() { String text = ""; assertThat(text, is(emptyString())); }
-
Assert that a String is empty or null:-
@Test public void givenString_whenEmptyOrNull_thenPass() { String text = null; assertThat(text, is(emptyOrNullString())); }
-
Assert that a String is not null:-
@Test public void givenString_whenNotNull_thenPass() { String text = "notnull"; assertThat(text, notNullValue()); }
-
Assert that two Strings are equal ignoring the case:-
@Test public void givenTwoStrings_whenEqualToIgnoringCase_thenPass() { String text1 = "foo"; String text2 = "FOO"; assertThat(text1, equalToIgnoringCase(text2)); }
-
Assert that two Strings are equal ignoring the white space:-
@Test public void givenTwoStrings_whenEqualToIgnoringWhiteSpace_thenPass() { String text1 = " my\tfoo bar "; String text2 = " my foo bar"; //assertThat(text1, equalToIgnoringWhiteSpace(text2)); // deprecated assertThat(text1, equalToCompressingWhiteSpace(text2)); // use this! }
Please Note that
equalToIgnoringWhiteSpace
matcher is deprecated, useequalToCompressingWhiteSpace
instead. -
Assert that a String contains the given Substring:-
@Test public void givenString_whenContainsGivenSubstring_thenPass() { String text = "lordOfTheRings"; String subtext = "Ring"; assertThat(text, containsString(subtext)); }
-
Assert that a String contains the given Substring ignoring the case:-
@Test public void givenString_whenContainsGivenSubstringIgnoringCase_thenPass() { String text = "lordOfTheRings"; String subtext = "RING"; assertThat(text, containsStringIgnoringCase(subtext)); }
-
Assert that a String contains one or more Substrings in a given order:-
@Test public void givenString_whenContainsOneOrMoreSubstringsInGivenOrder_thenPass() { String text = "lordOfTheRings"; assertThat(text, stringContainsInOrder(Arrays.asList("lord", "Ring"))); }
-
Assert that a String starts with a given Substring:-
@Test public void givenString_whenStartsWithGivenSubstring_thenPass() { String text = "lordOfTheRings"; String subtext = "lord"; assertThat(text, startsWith(subtext)); }
-
Assert that a String starts with a given Substring ignoring the case:-
@Test public void givenString_whenStartsWithGivenSubstringIgnoringCase_thenPass() { String text = "lordOfTheRings"; String subtext = "LORD"; assertThat(text, startsWithIgnoringCase(subtext)); }
-
Assert that a String ends with a given Substring:-
@Test public void givenString_whenEndsWithGivenSubstring_thenPass() { String text = "lordOfTheRings"; String subtext = "Rings"; assertThat(text, endsWith(subtext)); }
-
Assert that a String ends with a given Substring ignoring the case:-
@Test public void givenString_whenEndsWithGivenSubstringIgnoringCase_thenPass() { String text = "lordOfTheRings"; String subtext = "RINGS"; assertThat(text, endsWithIgnoringCase(subtext)); }
Number Matchers for Integer, Long, Double, Float, BigDecimal
Hamcrest Number Matchers are used to write assertions on Numbers (Integer, Long, Double, Float, and BigDecimal) with various conditions. Let’s look at them:-
- Assert that an Integer is greater than a given Integer:-
@Test public void givenInt_whenGreaterThanGivenInt_thenPass() { assertThat(2, greaterThan(1)); }
- Assert that an Integer is greater than or equal to a given Integer:-
@Test public void givenInt_whenGreaterThanOrEqualToGivenInt_thenPass() { assertThat(1, greaterThanOrEqualTo(1)); }
- Assert that an Integer is less than a given Integer:-
@Test public void givenInt_whenLessThanGivenInt_thenPass() { assertThat(-1, lessThan(1)); }
- Assert that an Integer is less than or equal to a given Integer:-
@Test public void givenInt_whenLessThanOrEqualToGivenInt_thenPass() { assertThat(1, lessThanOrEqualTo(1)); }
- Assert that a Double is within the given range specified by the operand and (+/-) error arguments:-
@Test public void givenDouble_whenWithinRange_thenPass() { // 0.8 is within range of 1.0 (+/-) 0.2 = 0.8 to 1.2 assertThat(0.8, is(closeTo(1.0 /*operand*/, 0.2 /*error*/))); }
- Assert that a BigDecimal is within the given range specified by the operand and (+/-) error arguments:-
@Test public void givenBigDecimal_whenWithinRange_thenPass() { // 1.8 is within range of 2.0 (+/-) 0.5 = 1.5 to 2.5 assertThat(new BigDecimal("1.8"), is(closeTo(new BigDecimal("2.0") /*operand*/, new BigDecimal("0.5") /*error*/))); }
Object Matchers for Java Object
Hamcrest Object Matchers are used to write assertions on Java Objects to check various conditions. Let’s look at them:-
- Assert that two Objects are equal using the Object’s
equals()
method:-@Test public void givenTwoObjects_whenEquals_thenPass() { User user1 = User.builder().firstName("Adam").build(); User user2 = User.builder().firstName("Adam").build(); assertThat(user1, equalTo(user2)); }
- Assert that two objects are referring to the same instance:-
@Test public void givenTwoObjects_whenSameInstance_thenPass() { User user = User.builder().firstName("Adam").build(); assertThat(user, sameInstance(user)); }
- Assert that an Object is an instance of the given Class:-
@Test public void givenObject_whenInstanceOfGivenClass_thenPass() { User user = User.builder().firstName("Adam").lastName("Smith").build(); assertThat(user, instanceOf(User.class)); }
- Assert that
toString()
method of an Object returns the given String:-@Test public void givenObject_whenToStringMethodReturnsGivenString_thenPass() { User user = User.builder().firstName("Adam").lastName("Smith").build(); String str = user.toString(); assertThat(user, hasToString(str)); }
- Assert that a Class is a subclass of given another class:-
@Test public void given2Classes_whenFirstClassChildOfSecondClass_thenCorrect(){ assertThat(Integer.class, typeCompatibleWith(Number.class)); }
Bean Matchers for Java Object’s instance
Hamcrest Bean Matchers are used to test the properties of a Java Object instance.
Let’s create an instance of User
class and assert them:-
@Data
@Builder
public class User {
private String firstName;
private Integer age;
private Boolean isPremiumUser;
}
- Assert that an Object’s instance has a property with the given name:-
@Test public void givenObject_whenHasGivenProperties_thenPass() { User user = User.builder().build(); assertThat(user, hasProperty("firstName")); assertThat(user, hasProperty("age")); assertThat(user, hasProperty("isPremiumUser")); }
- Assert that an Object’s instance has a property with the given name and value:-
@Test public void givenObject_whenHasPropertyWithGivenNameAndValue_thenPass() { User user = User.builder().firstName("Adam").age(22).isPremiumUser(true).build(); assertThat(user, hasProperty("firstName", equalTo("Adam"))); assertThat(user, hasProperty("age", equalTo(22))); assertThat(user, hasProperty("isPremiumUser", equalTo(true))); }
- Assert that two Object’s instances have all properties with the same values:-
@Test public void givenTwoObjects_whenSamePropertyValues_thenPass() { User user1 = User.builder().firstName("Adam").age(22).isPremiumUser(true).build(); User user2 = User.builder().firstName("Adam").age(22).isPremiumUser(true).build(); assertThat(user1, samePropertyValuesAs(user2)); }
- Assert that two Object’s instances have properties with the same values excluding one or more ignore properties specified:-
The above two instances have the same
@Test public void givenTwoObjects_whenSamePropertyValuesExcludingIgnoredProps_thenPass() { User user1 = User.builder().firstName("Adam").age(22).isPremiumUser(true).build(); User user2 = User.builder().firstName("Adam").age(18).isPremiumUser(false).build(); assertThat(user1, samePropertyValuesAs(user2, "age", "isPremiumUser")); }
firstName
value and we ignored theage
andisPremiumUser
properties from comparison.
Collection Matchers for List
Hamcrest Collection Matchers can be used for List assertions. Let’s look at examples:-
-
Assert that a List is empty:-
@Test public void givenList_whenEmpty_thenPass() { List<String> list = new ArrayList<>(); assertThat(list, empty()); }
-
Assert that a List is of a given size:-
@Test public void givenList_whenSizeMatches_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, hasSize(4)); assertThat(list, iterableWithSize(4)); }
Both matchers
hasSize
anditerableWithSize
have the same results and can be used alternatively. -
Assert that a List contains all the given values in the same order:-
@Test public void givenList_whenContainsAllValuesInSameOrder_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, contains("lord", "of", "the", "rings")); }
The
contains
matcher should have the same number of specified items as the length of the list for a positive match. -
Assert that a List contains all the given values in any order:-
@Test public void givenList_whenContainsAllValuesInAnyOrder_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, containsInAnyOrder("rings", "of", "the", "lord")); }
Same as
contains
, thecontainsInAnyOrder
matcher should have the same number of specified items as the length of the list for a positive match. Though they can be in any order. -
Assert that a List contains one or more given values in the relative order:-
@Test public void givenList_whenContainsValuesInRelativeOrder_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, containsInRelativeOrder("of", "rings")); }
-
Assert that a List contains the given value:-
@Test public void givenList_whenContainsGivenValue_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, hasItem("lord")); assertThat("of", is(in(list))); }
Both the matchers in the example have the same result and can be used alternatively.
-
Assert that a List contains the given value with the specified condition:-
@Test public void givenList_whenContainsGivenValueWithCondition_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, hasItem(equalTo("of"))); assertThat(list, hasItem(startsWith("th"))); assertThat(list, hasItem(endsWith("ngs"))); }
Matchers can be nested together for more complex conditions e.g.
hasItem
is nested withequalsTo
,startsWith
, andendsWith
in the above example. -
Assert that List contains one or more given values with the specified condition:-
@Test public void givenList_whenContainsOneOrMoreGivenValuesWithCondition_thenPass() { List<String> list = Arrays.asList("lord", "of", "the", "rings"); assertThat(list, hasItems(startsWith("ring"), endsWith("ord"), equalTo("of"))); }
-
Assert that every item in the List matches the given condition:-
@Test public void givenList_whenEveryItemMatchesCondition_thenPass() { List<String> list = Arrays.asList("bar", "baz"); assertThat(list, everyItem(startsWith("ba"))); }
Collection Matchers for Array
Hamcrest Collection Matchers can also be used for Array assertions. Let’s look at examples:-
-
Assert that Array is of the given size:-
@Test public void givenArray_whenGivenSize_thenPass() { String[] arrayItems = { "lord", "of", "the", "rings" }; assertThat(arrayItems, arrayWithSize(4)); }
-
Assert that an Array has the given item:-
@Test public void givenArray_whenHasItemInArray_thenPass() { String[] arrayItems = { "lord", "of", "the", "rings" }; assertThat(arrayItems, hasItemInArray("lord")); assertThat("of", oneOf(arrayItems)); assertThat("the", is(in(arrayItems))); }
All matchers in the example have the same result and can be used alternatively.
-
Assert that an Array contains all the given items in the same order:-
@Test public void givenArray_whenContainsAllItemsInSameOrder_thenPass() { String[] arrayItems = { "lord", "of", "the", "rings" }; assertThat(arrayItems, arrayContaining("lord", "of", "the", "rings")); }
The
arrayContaining
matcher should have the same number of specified items as the length of the array for a positive match. -
Assert that an Array contains all the given values in any order:-
@Test public void givenArray_whenContainsAllValuesInAnyOrder_thenPass() { String[] arrayItems = { "lord", "of", "the", "rings" }; assertThat(arrayItems, arrayContainingInAnyOrder("rings", "of", "the", "lord")); }
Same as
arrayContaining
, thearrayContainingInAnyOrder
matcher should have the same number of specified items as the length of the array for a positive match. Though they can be in any order.
Collection Matchers for Map
Hamcrest Collection Matchers provide assertions for Map to check key, value and entry. Let’s look at examples:-
- Assert that a Map is empty:-
@Test public void givenMap_whenEmpty_thenPass() { Map<String, String> myMap = new HashMap<>(); assertThat(myMap, is(anEmptyMap())); }
- Assert that the Map has given size:-
@Test public void givenMap_whenSizeMatched_thenPass() { Map<String, String> myMap = Map.of("key1", "value1", "key2", "value2"); assertThat(myMap, is(aMapWithSize(equalTo(2)))); }
- Assert that the Map has a given Key:-
@Test public void givenMap_whenHasGivenKey_thenPass() { Map<String, String> myMap = Map.of("myKey", "myValue"); assertThat(myMap, hasKey(startsWith("my"))); assertThat(myMap, hasKey(endsWith("Key"))); }
- Assert that the Map has a given Key with the specified condition:-
@Test public void givenMap_whenHasGivenKeyWithCondition_thenPass() { Map<String, String> myMap = Map.of("myKey", "myValue"); assertThat(myMap, hasKey(startsWith("my"))); assertThat(myMap, hasKey(endsWith("Key"))); }
- Assert that the Map has a given Value:-
@Test public void givenMap_whenHasGivenValue_thenPass() { Map<String, String> myMap = Map.of("myKey", "myValue"); assertThat(myMap, hasValue("myValue")); }
- Assert that the Map has a given Value with the specified condition:-
@Test public void givenMap_whenHasGivenValueWithCondition_thenPass() { Map<String, String> myMap = Map.of("myKey", "myValue"); assertThat(myMap, hasValue(startsWith("my"))); assertThat(myMap, hasValue(endsWith("Value"))); }
- Assert that the Map has a given Entry:-
@Test public void givenMap_whenHasGivenEntry_thenPass() { Map<String, String> myMap = Map.of("myKey", "myValue"); assertThat(myMap, hasEntry("myKey", "myValue")); assertThat(myMap, hasEntry(endsWith("Key"), endsWith("Value"))); }
- Assert that the Map has a given Entry with the specified condition:-
@Test public void givenMap_whenHasGivenEntryWithCondition_thenPass() { Map<String, String> myMap = Map.of("myKey", "myValue"); assertThat(myMap, hasEntry("myKey", "myValue")); assertThat(myMap, hasEntry(endsWith("Key"), endsWith("Value"))); }
Logical Matchers - NOT, AND, OR
Hamcrest Logical Matchers can be used for chaining the matcher conditions with logical operators.
Matcher | Logical Operator |
---|---|
not |
NOT |
anyOf |
OR |
allOf |
AND |
anyOf(allOf(), not()) |
OR (AND, NOT) |
either...or |
OR |
both...and |
AND |
Let’s look at the examples:-
- Assert that two Strings are not equal:-
@Test public void givenTwoStrings_whenNotEquals_thenPass() { String text1 = "text1"; String text2 = "text2"; assertThat(text1, not(equalTo(text2))); }
- Assert that a String matches with any of the given conditions:-
@Test public void givenString_whenAnyOfGivenConditionsMatch_thenPass() { String text = "lord of the rings"; assertThat(text, anyOf(startsWith("lord"), containsString("power"))); }
- Assert that a String matches with all of the given conditions:-
@Test public void givenString_whenAllOfGivenConditionsMatch_thenPass() { String text = "lord of the rings"; assertThat(text, allOf(startsWith("lord"), endsWith("rings"))); }
- Assert that a String matches the nested condition using AND and OR operators:-
@Test public void givenString_whenComplexConditionMatch_thenPass() { String text = "lord of the rings"; assertThat(text, anyOf(allOf(startsWith("lord"), endsWith("rings")), endsWith("power"))); }
- Assert that a String matches either of the two given conditions:-
@Test public void givenString_whenEitherConditionMatch_thenPass() { String text = "lord of the rings"; assertThat(text, either(startsWith("lord")).or(endsWith("power"))); }
- Assert that a String matches both of the given conditions:-
@Test public void givenString_whenBothConditionMatch_thenPass() { String text = "lord of the rings"; assertThat(text, both(startsWith("lord")).and(endsWith("rings"))); }
Writing Custom Matcher
Hamcrest comes with lots of useful matchers, but also provides you the ability to write your custom matcher to fit your testing needs.
Let’s write a custom matcher for testing if a given number is a prime number.
package com.example.assertion;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.stream.IntStream;
public class IsPrimeNumber extends TypeSafeMatcher<Integer> {
@Override
protected boolean matchesSafely(Integer number) {
return number > 1 && IntStream.rangeClosed(2, (int) Math.sqrt(number))
.noneMatch(n -> (number % n == 0));
}
@Override
public void describeTo(Description description) {
description.appendText("a prime number");
}
public static Matcher isPrimeNumber() {
return new IsPrimeNumber();
}
}
We create a custom matcher by extending the TypeSafeMatcher
abstract class. We need to implement the matchSaely
method which provides a predicate if the number is a prime number. We also implement describeTo
method which produces a failure message in case a test fails.
Let’s test if a number is prime using our custom matcher isPrimeNumber
:-
import static com.example.assertion.IsPrimeNumber.isPrimeNumber;
@Test
public void givenNumber_whenPrimeNumber_thenPass() {
Integer num = 7;
assertThat(num, new isPrimeNumber());
}
and here is a failure message when test a non-prime number:-
java.lang.AssertionError: Expected: a prime number but: was <8>
That’s all about the Hamcrest matchers. Thanks for Reading!