In this article, you will learn what Hamcrest custom matchers are. You will also learn how to create your very own custom matcher.
Let’s dive in
- Introduction
- When to use Hamcrest custom matchers
- Creating a Hamcrest custom matcher
- Using a Hamcrest custom matcher
- Sequitur
Introduction
In a previous article, we learned how versatile Hamcrest matchers are. Listed below are some of the great benefits that Hamcrest matchers provide.
- Improved code readability
- Allows for self-documenting code
- It makes debugging easier by way of better error messaging
Did you know that there is another powerful feature that Hamcrest has? It allows us to create our very own custom matchers to fit our testing needs. You can think of custom Hamcrest matchers as a browser plug-in; in the sense that it makes a good thing even better.
Did you know that there is another powerful feature that Hamcrest has? It allows us to create our very own custom matchers to fit our testing needs. You can think of custom Hamcrest matchers as a browser plug-in; in the sense that it makes a good thing even better.

When to use Hamcrest custom matchers
Here are some of the reasons you may want to create a custom Hamcrest matcher.
- To bundle multiple assertions into one.
- To create a sort of Domain Specific Language (DSL) within your test framework.
- You are not able to find a built-in matcher that suits our needs.
Creating a Hamcrest custom matcher
The process of creating a custom matcher is pretty straightforward. To do this, we need to use the TypeSafeMatcher class in Hamcrest. Since this class is abstract, there are two methods that we are going to need to implement when extending this class; they are matchesSafely and describeTo. The former performs the actual assertion, and the latter provides a custom error message when a test fails.
The first thing we will need is to add the following Maven dependency.
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
Click here to see the latest version.
Now, let us assume we have the following two scenarios to test.
- Test that an account number starts with “XYZ” and ends in “-2020”
- Test that a patient’s temperature is above 100.4° Fahrenheit corresponds to having a fever.
For the first scenario, we will have a class called Temperature. We know that we are testing a temperature value, so we need a Matcher<Double>, and we can achieve this by subclassing the TypeSafeMatcher class as follows.
package io.automatenow.hamcrestmatchers.custommatchers;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class Temperature extends TypeSafeMatcher<Double> {
@Override
public boolean matchesSafely(Double temperature) {
return (temperature > 100.4);
}
public void describeTo(Description description) {
description.appendText("a temperature greater than 100.4F");
}
public static Matcher aFever() {
return new Temperature();
}
}
Notice that matchesSafely will return true if the temperature is above 100.4 and false otherwise. We then use the describeTo method to add our error message. Lastly, we need a static method that will allow us to use our custom matcher just like any other built-in Hamcrest matcher; that is the purpose of the aFever method.
In the second scenario, we will have a class called AccountNumber as follows.
package io.automatenow.hamcrestmatchers.custommatchers;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class AccountNumber extends TypeSafeMatcher<String> {
@Override
public boolean matchesSafely(String number) {
return (number.startsWith("XYZ") &&
number.endsWith("-2020"));
}
public void describeTo(Description description) {
description.appendText("a valid account number");
}
public static Matcher aValidAccountNumber() {
return new AccountNumber();
}
}
Similarly, the matchesSafely method contains our matcher logic, and we will be using the aValidAccountNumber method to access our matcher.
Using a Hamcrest custom matcher
All that we need now that we have created our custom matchers is a way to test them. We will do that by writing some tests that will statically import our two matcher methods, aFever, and aValidAccountNumber. The code example is below.
package io.automatenow.hamcrestmatchers.custommatchers;
import org.testng.annotations.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static io.automatenow.hamcrestmatchers.custommatchers.AccountNumber.aValidAccountNumber;
import static io.automatenow.hamcrestmatchers.custommatchers.Temperature.aFever;
public class HamcrestCustomMatchers {
@Test public void testValidAccountNumber1() {
String accountNumber = "XYZ387457-2020";
assertThat(accountNumber, is(aValidAccountNumber()));
}
@Test public void testPatientIsSick1() {
double patientTemperature = 102.5;
assertThat(patientTemperature, is(aFever()));
}
}
Notice that both tests will pass since they fall within our required parameters. However, the following two tests will fail.
@Test
public void testValidAccountNumber2() {
String accountNumber = "XYZ387457-2019";
assertThat(accountNumber, is(aValidAccountNumber()));
}
@Test
public void testPatientIsSick2() {
double patientTemperature = 98.3;
assertThat(patientTemperature, is(aFever()));
}
The error message that we get for test testValidAccountNumber2 is
java.lang.AssertionError:
Expected: is a valid account number
but: was "XYZ387457-2019"
and the error for testPatientIsSick2 is
java.lang.AssertionError:
Expected: is a temperature greater than 100.4 F
but: was <98.3>
Sequitur
If you want tests that are easy to read and write and provide excellent error messaging, Hamcrest custom matchers are the way to go.
Now that you have seen how easy it is to create custom matchers, it is your turn to create your own!
You can find the source code for all the examples listed in this post by visiting the automateNow GitHub page.
References
Suggested Article / Testing With Hamcrest
Follow our blog
Be the first to know when we publish new content.