Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I use Hamcrest-Matcher and assertThat() instead of traditional assertXXX()-Methods

People also ask

Why do we use Hamcrest?

Hamcrest is a widely used framework for unit testing in the Java world. Hamcrest target is to make your tests easier to write and read. For this, it provides additional matcher classes which can be used in test for example written with JUnit. You can also define custom matcher implementations.

What is the difference between assertThat and assertEquals?

assertEquals() is the method of Assert class in JUnit, assertThat() belongs to Matchers class of Hamcrest. Both methods assert the same thing; however, hamcrest matcher is more human-readable. As you see, it is like an English sentence “Assert that actual is equal to the expected value”.

What can be used instead of assertThat?

assertThat method defined in Hamcrest 1.3. Therefore, it is recommended to directly use the equivalent assertion defined in the third party Hamcrest library. This rule finds the deprecated usages of Assert. assertThat and automatically replaces them with MatcherAssert.

What is the use of assertThat?

The assertThat is one of the JUnit methods from the Assert object that can be used to check if a specific value match to an expected one. It primarily accepts 2 parameters. First one if the actual value and the second is a matcher object.


There's no big advantage for those cases where an assertFoo exists that exactly matches your intent. In those cases they behave almost the same.

But when you come to checks that are somewhat more complex, then the advantage becomes more visible:

val foo = List.of("someValue");
assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));
Expected: is <true>
         but: was <false>

vs.

val foo = List.of("someValue");
assertThat(foo, containsInAnyOrder("someValue", "anotherValue"));
Expected: iterable with items ["someValue", "anotherValue"] in any order
     but: no item matches: "anotherValue" in ["someValue"]

One can discuss which one of those is easier to read, but once the assert fails, you'll get a good error message from assertThat, but only a very minimal amount of information from assertTrue.


The JUnit release notes for version 4.4 (where it was introduced) state four advantages :

  • More readable and typeable: this syntax allows you to think in terms of subject, verb, object (assert "x is 3") rather than assertEquals, which uses verb, object, subject (assert "equals 3 x")
  • Combinations: any matcher statement s can be negated (not(s)), combined (either(s).or(t)), mapped to a collection (each(s)), or used in custom combinations (afterFiveSeconds(s))
  • Readable failure messages. (...)
  • Custom Matchers. By implementing the Matcher interface yourself, you can get all of the above benefits for your own custom assertions.

More detailed argumentation from the guy who created the new syntax : here.


Basically for increasing the readability of the code.

Besides hamcrest you can also use the fest assertions. They have a few advantages over hamcrest such as:

  • they are more readable
    (assertEquals(123, actual); // reads "assert equals 123 is actual" vs
    assertThat(actual).isEqualTo(123); // reads "assert that actual is equal to 123")
  • they are discoverable (you can make autocompletion work with any IDE).

Some examples

import static org.fest.assertions.api.Assertions.*;

// common assertions
assertThat(yoda).isInstanceOf(Jedi.class);
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
assertThat(frodo).isIn(fellowshipOfTheRing);
assertThat(sauron).isNotIn(fellowshipOfTheRing);

// String specific assertions
assertThat(frodo.getName()).startsWith("Fro").endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .excludes(sauron);


// map specific assertions (One ring and elves ring bearers initialized before)
assertThat(ringBearers).hasSize(4)
                       .includes(entry(Ring.oneRing, frodo), entry(Ring.nenya, galadriel))
                       .excludes(entry(Ring.oneRing, aragorn));

October 17th, 2016 Update

Fest is not active anymore, use AssertJ instead.


A very basic justification is that it is hard to mess up the new syntax.

Suppose that a particular value, foo, should be 1 after a test.

assertEqual(1, foo);

--OR--

assertThat(foo, is(1));

With the first approach, it is very easy to forget the correct order, and type it backwards. Then rather than saying that the test failed because it expected 1 and got 2, the message is backwards. Not a problem when the test passes, but can lead to confusion when the test fails.

With the second version, it is almost impossible to make this mistake.