Complete guide to Hamcrest matchers

Complete guide to Hamcrest matchers

This article is a complete guide to using Hamcrest matchers. You will also learn how to use Hamcrest matchers effectively in software testing.

Table of contents

Introduction

It turns out that the word “Hamcrest” is an anagram of the word “Matchers”! This means that we cannot really define Hamcrest without first introducing matchers.

A matcher is an object that allows “match” rules to be defined declaratively. You can think of a matcher as a way for making assertions in a program.

Therefore, Hamcrest is a framework for writing matcher objects. We use the assertThat method in order to make test assertions.

Hamcrest vs TestNG

By now you are probably wondering if Hamcrest can be a substitute for TestNG or whether one is better than the other. Well, it turns out that although both do similar things; one cannot substitute the other entirely. TestNG is a testing framework and as noted above, Hamcrest is a matcher framework. Both frameworks allow us to make assertions; it is here where Hamcrest does a better job than TestNG.

Hamcrest is very useful for UI validation and data filtering. Most importantly though, Hamcrest allows your tests to be self-documenting since it focuses on what your code should do instead of what it is doing. Let’s look at some examples.

Code readability

Here we have an array of strings.

String[] primaryColors = {"red", "green", "blue"};

In order to verify that this array has a certain value, we would write a TestNG assertion similar to this:

Assert.assertTrue(Arrays.asList(primaryColors).contains("blue"));

Meanwhile, using Hamcrest we would write something like this:

assertThat(primaryColors, hasItemInArray("blue"));

Notice that Hamcrest allows us to focus on the ‘what’ rather than the ‘how’. This results in code that is easier to write and understand.

Self-documenting code

Hamcrest allows us tremendous flexibility to write code that essentially needs no documentation. Keep in mind that your documentation is only good until the next code change!

Take for example the following statements.

String pageTitle = "Home";
assertThat(pageTitle, equalTo("Home"));
assertThat(pageTitle, is("Home"));
assertThat(pageTitle, is(equalTo("Home")));

All of the above will result in a passing test. In the case of is(“Home”), it passes because it is an overloaded method that returns is(equalTo(“Home”)).

It is clear that making assertions in this way requires no explanation as to what your code is doing since the code speaks for itself.

Better error messages

Perhaps the most compelling feature of using Hamcrest matchers is the type of error messages that it provides out of the box.

Let us assume that we have the following list.

List<Integer> fibonacci = Arrays.asList(0,1,1,2,3,5,8);

If we perform the following assertion,

assertThat(fibonacci, hasSize(5));

we get the following error.

java.lang.AssertionError:
Expected: a collection with size <5>
but: collection size was <7>

Compare that to using the following TestNG assertion.

Assert.assertEquals(fibonacci.size(), 5);

The error message that is displayed is as follows.

java.lang.AssertionError: expected [5] but found [7]
Expected :5
Actual   :7

Needless to say, this error message it is not very helpful when we are trying to debug our code.

You might say, well that’s fine but TestNG allows me to provide my own error message as in the following example.

Assert.assertEquals(fibonacci.size(), 5, "the list size did not equal 5");

This results in this error message.

java.lang.AssertionError: the list size did not equal 5 expected [5] but found [7]
Expected :5
Actual   :7

The point is that with Hamcrest we do not really need to provide our own error message if we do not want to; any errors that may occur will still provide very useful information.

So, how do we provide our own error message using Hamcrest? That is accomplished using the describeAs method.

assertThat(fibonacci, describedAs("the list size to be equal to 5", hasSize(5)));

The error message looks like this.

java.lang.AssertionError:
Expected: the list size to be equal to 5
but: collection size was <7>

Better enforcement of type safety

Another nice thing of using Hamcrest matchers is its better handling of type safely. If we were to write the following code using a framework such as TestNG,

Assert.assertEquals("abc", 123);

the code will compile but it will fail at runtime. Meanwhile, if we write a similar code using Hamcrest,

assertThat(123, is("abc"));

the code will not compile since it knows that we are trying to compare an int type to a String type object.

Custom matchers

Hamcrest has another powerful feature that allows us to write our own custom matchers. Doing so permits the creation of our very own DSL (Domain Specific Language) if we so choose.

I have written another blog post that covers custom matchers in detail, you can find it here.

Common matchers

Here is a list of some of the most common Hamcrest matchers.

allOf – this functions as the logical ‘&&’ operator in Java in which it matches if all the matchers match.

String email = "info@automatenow.io";
assertThat(email, allOf(startsWith("info"), containsString("@"), endsWith(".io")));

anyOf – this functions as the logical ‘|| operator in Java in which it matches if any of the matchers match.

assertThat(email, anyOf( endsWith(".io"), endsWith(".dev"), endsWith(".com")));

equalTo – used to test object equality using Object.equals

assertThat(“Home page”, equalTo(“Home page”));

hasToString – used to test Object.toString

assertThat(true, hasToString(equalTo("true")));

hasKey, hasValue – checks to see if a map contains a given key or value

HashMap<Integer, String> binary = new HashMap<Integer, String>();
binary.put(1, "on");
assertThat(binary, hasKey(1));
assertThat(binary, hasValue("on"));

Getting started with Hamcrest

Now that we have seen how convenient Hamcrest matchers are, you may be wondering how to start using them in your automation project.

Hamcrest was originally designed for testing Java applications but it has since been ported over to other programing languages. For instance, there is PyHamcrest for Python, NHamcrest for .NET, OCHamcrest for Objective-C, etc.

If you happen to be using a Java Maven project, you can add the following Maven dependency.

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest</artifactId>
    <version>2.2</version>
    <scope>test</scope>
</dependency> 

Please pay special attention to the version number since it may have already been updated by the time you read this post. Click here to see the latest version.

You can find the source code for all the examples listed in this post by visiting the AUTOMATENOW GitHub page.

References

http://hamcrest.org/

https://bit.ly/3vlAXuc

https://bit.ly/32VWo97

One thought on “Complete guide to Hamcrest matchers

Leave a Reply