Junit Assertion Testing Using Hamcrest library Matcher

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

matchesPattern,matchesRegex - to test string again given regex


@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"
  

Post a Comment

Previous Post Next Post

Recent Posts

Facebook