Hamcrest is an assert framework for Junit testing which is based on the
concept of matcher. Hamcrest provide various useful matcher to make Junit
testing more readable and less manual code, also we can define your custom
matcher in hamcrest.
To use Hamcrest matchers in JUnit you use the assertThat statement followed by one or several matchers.
Add below dependency to our POM.xml file.
<dependencies>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
Some common matchers in hamcrest
hasProperty - test Java Bean(object) properties
Not - check for negative logic
All ham crest matcher can be found in org.hamcrest.Matchers package.
Object matchers
equalTo - used to check equality between objects.
hasToString - used to check string format of object
instanceOf, isCompatibleType - used to test the objects instance
type
notNullValue, nullValue - used to test objects null or not
sameInstance - used to test whether both object is same instance.
public class Employee {
public int id;
public String name;
public String address;
public Employee(int id, String name, String address) {
super();
this.id = id;
this.name = name;
this.address = address;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", address=" + address + "]";
}
}
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import com.example.demo.controller.Employee;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.samePropertyValuesAs;
import static org.hamcrest.Matchers.sameInstance;
public class ObjectsExample {
@Test
void objectHasGivenProperty() {
Employee employee = new Employee(1, "jack", "Chennai");
assertThat(employee, notNullValue());
assertThat(employee, hasProperty("name"));
assertThat(employee, hasProperty("address", equalTo("Chennai")));
}
@Test
void isBothObjectHavingSameValue() {
Employee employee = new Employee(1, "jack", "Chennai");
Employee employee1 = new Employee(1, "jack", "Chennai");
assertThat(employee, samePropertyValuesAs(employee1));
//check both instance
assertThat(employee, sameInstance(employee));
}
@Test
public void checkObjectType() {
Employee employee = new Employee(1, "jack", "Chennai");
assertThat(employee, instanceOf(Employee.class));
// shortcut for instanceOf
assertThat(employee, isA(Employee.class));
}
}
Collection matchers
collections - hamcrest have matchers for various collections (list, map, array)
hasSize - test collection size
hasEntry, hasKey, hasValue - test a map contains an entry,
key or value
hasItem, hasItems - test a collection contains elements
hasItemInArray - test an array contains an element
List Matchers
Below are matchers for list.
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.emptyArray;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItemInArray;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasValue;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
@Test
public void list_testcase() {
List < Integer > list = Arrays.asList(5, 2, 4);
// check whether list is non empty
assertThat(list, empty());
// check whether list is non empty
assertThat(list, not(empty()));
// check list size
assertThat(list, hasSize(3));
// ensure the list contain element in any order
assertThat(list, contains(5, 2, 4));
// ensure element order
assertThat(list, containsInAnyOrder(2, 4, 5));
// check list has given elements
assertThat(list, hasItem(2));
}
Map Matchers
Below are matchers for map.
@Test
public void map_testcase() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "Amit");
map.put(5, "Rahul");
map.put(2, "Jai");
map.put(6, "Amit");
assertThat(map, not(anEmptyMap()));
assertThat(map, aMapWithSize(4));
assertThat(map, hasKey(1));
assertThat(map, hasValue("Jai"));
assertThat(map, hasEntry(1, "Amit"));
}
Array Matchers
Below are matchers for arrays.
@Test
public void array_testcase() {
Integer nums[] = new Integer[] { 2, 4, 5, 6, 7, 8 };
Integer nums2[] = new Integer[] { 2, 4, 5, 6, 7, 8 };
assertThat(nums, not(emptyArray()));
assertThat(nums, arrayWithSize(6));
assertThat(nums, hasItemInArray(1));
assertThat(nums, arrayContaining(nums2));
assertThat(nums, arrayContainingInAnyOrder(nums2));
}
String Matcher
emptyOrNullString - to test string value
equalToIgnoringCase - test string equality ignoring case
equalToIgnoringWhiteSpace - test string equality ignoring differences
in runs of whitespace
containsString, endsWith, startsWith - to test string
matching
@Test
public void string_testcase() {
String mailid="xxxyyy@gmail.com";
assertThat(mailid,is(emptyOrNullString()));
assertThat(mailid,containsString("gmail.com"));
assertThat(mailid,containsStringIgnoringCase("gmail.com"));
assertThat(mailid,startsWith("xxx"));
assertThat(mailid,endsWith(".com"));
String number="12345";
assertThat(number, matchesPattern(Pattern.compile("ˆ[1-5]$")));
assertThat(number, matchesRegex("ˆ[1-5]$"));
}
Grouping matchers in assertion
The allOf matcher expect all the condition need to be true if one
condition fails then allof matcher fail the assertion.
import static org.hamcrest.CoreMatchers.allOf;
@Test
public void matchAllCondition() {
Employee employee = new Employee(1, "jack", "Chennai");
assertThat(employee, allOf(isA(Employee.class),
notNullValue(),
hasProperty("name"),
hasProperty("address", equalTo("Chennai"))));
}
The anyOf matchers expect any one of the conditions to be true if all condition fails then assert fail.
import static org.hamcrest.CoreMatchers.anyOf;
@Test
public void shouldMatchAnyOneCondition() {
Employee employee = new Employee(1, "jack", "Chennai");
assertThat(employee, anyOf(
hasProperty("name", equalTo("john")),
hasProperty("address", equalTo("Chennai"))));
}
Writing custom matchers
We can also write custom matcher by extending TypeSafeMatcher this class
provides type safety by default and checks for null values before applying
to matcher
Below is an example for defining a matcher which check given date is in
given format or not.
package com.example.demo;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class DateMatcher extends TypeSafeMatcher < String > {
private final String format;
public DateMatcher(final String format) {
this.format = format;
}
@Override
public void describeTo(Description description) {
//this description will be show in error log is test fails
description.appendText("Date format matching=`" + format + "`");
}
@Override
protected boolean matchesSafely(String date) {
try {
LocalDate ldt = null;
DateTimeFormatter fomatter = DateTimeFormatter.ofPattern(format);
ldt = LocalDate.parse(date, fomatter);
String result = ldt.format(fomatter);
return result.equals(date);
} catch (DateTimeParseException e) {
return false;
}
}
public static DateMatcher matchesRegex(final String format) {
return new DateMatcher(format);
}
}
Now let write few test to case our matcher
Below test pass because the date is in given format
@Test
public void dateTest() {
String date = "25/09/2013";
assertThat(date, DateMatcher.matchesRegex("dd/MM/yyyy"));
}
Below test fail because date is not in given format
@Test
public void dataTest() {
String s = "25/25/2013";
assertThat(s, DateMatcher.matchesRegex("dd/MM/yyyy"));
}
We will be getting below error message is date is not in given format
java.lang.AssertionError:
Expected: Date format matching=`dd/MM/yyyy`
but: was "25/25/2013"