Why Junit uses Hamcrest

Hamcrest is Junit`s Assert framework. Hamcrest provides a variety of matchers, allowing you to test the state of an object with compact code. Also, when a test fails, a detailed Failure message is printed out for what reason the test failed.

The term Hamcrest was coined by simply changing the alphabetic position of Matchers.

Matcher

A Matcher is an object that performs a match operation.

If you pass Object and Matcher as arguments to assertThat() as follows, the Matcher checks whether the Object meets the expected condition. Depending on the matcher`s result, the test will pass or fail.

public void assertThat(Object o, Matcher matcher){
    ...
}

Why use Hamcrest?

By comparing the test using Junits Assert and the test using Hamcrest, lets see the reason for using Hamcrest.

  • Reading Failure Messages
  • Readability of test code
  • Various Matcher products

Readability of Failure messages

The code below is a basic Assert provided by Junit, and it is a test to check whether variables a and b are different.

@Test
public void test_using_junit() {
    int a = 10;
    int b = 10;

    assertNotEquals(a, b);
}

The above test will of course fail, but if you look at the failure message, it is not clear what went wrong. If you look at the code with the log, you will understand that the test failed because a and b have the same value.

expected: not equal but was: <10>
org.opentest4j.AssertionFailedError: expected: not equal but was: <10>

The above code can also be implemented as a test using Hamcrest like this:

@Test
public void test_using_hamcrest() {
    int a = 10;
    int b = 10;

    assertThat(a, is(not(equalTo(b))));
}

Of course, this test will also fail, but the failure log is easier to understand than the above log.

Expected: is not <10>
     but: was <10>

Readability of test code (1)

assertNotEquals() is an Assert provided by Junit by default. This Assert checks whether a and b passed as arguments are different. Its a simple code, so its not hard to understand.

assertNotEquals(a, b);

The following is a test code that checks the same conditions as above using Hamcrest. If you look at the code, it becomes a perfect English sentence unlike the above.

assertThat(a, is(not(equalTo(b))));

You might think that the above code is a bit unreadable because it is made of overly perfect sentences. If you remove is() as follows, the result is the same, and the code looks more concise.

assertThat(a, not(equalTo(b)));

Readability of test code (2)

You can write code to test if the str variable satisfies all three conditions, like this: Of course, it`s fine to test each with 3 asserts, but I assumed there are situations where you need to implement with 1 assert.

public void test_allOf() {
    String str = "MyTest";
    boolean result = str.equals("MyTest")
            && str.startsWith("My")
            && str.contains("Test");

    assertTrue(result);
}

Hamcrest provides an allOf() matcher, and the test succeeds only when all matchers passed as arguments pass. That is, allOf() means AND in logical operator. Instead of &&, all Of can be used to help understand the code.

@Test
public void test_allOf() {
    String str = "MyTest";

    assertThat(str, allOf(is("MyTest"),
                startsWith("My"),
                containsString("Test")));
}

Conversely, the Matcher corresponding to the logical operator OR is anyOf(). If only one of the Matchers passed as arguments passes, the test will pass.

assertThat(str, anyOf(is("MyTest"),
            startsWith("Me"),
            containsString("Test")));

Provide various matchers

If you want to test that the absolute difference between a and b is less than 0.5, you can implement assertTrue like this:

@Test
public void test_closeTo() {
    double a = 10.9;
    double b = 10.0;

    assertTrue(Math.abs(a-b) < 0.5);
}

However, this code is not very readable, and when it fails, it outputs a log that is difficult to understand why it failed, like this:

expected: <true> but was: <false>
Expected :true
Actual   :false

Hamcrest provides a matcher called closeTo(), which can be used to test whether the absolute difference is less than 0.5.

@Test
public void test_using_hamcrest3() {
    double a = 10.9;
    double b = 10.0;

    assertThat(a, closeTo(b, 0.5));
}

When a test fails, a log of why it failed is also output.

Expected: a numeric value within <0.5> of <10.0>
     but: <10.9> differed by <0.40000000000000036> more than delta <0.5>

Hamcrest provides various matchers in addition to closeTo(). For more information about the API, refer to Hamcrest JavaDoc.

  • allOf, anyOf
  • not, is
  • hasEntry, hasKey, hasValue
  • closeTo
  • greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo
  • equalToIgnoringCase, equalToIgnoringWhiteSpace
  • containsString, endsWith, startsWith
codechachaCopyright ©2019 codechacha