Why should I use Hamcrest matcher and assertThat() instead of traditional assertXXX() methods?
Asked Answered
W

7

170

When I look at the examples in the Assert class JavaDoc

assertThat("Help! Integers don't work", 0, is(1)); // fails:
// failure message:
// Help! Integers don't work
// expected: is <1> 
// got value: <0>
assertThat("Zero is one", 0, is(not(1))) // passes

I don't see a big advantage over, let's say, assertEquals( 0, 1 ).

It's nice maybe for the messages if the constructs get more complicated, but do you see more advantages? Readability?

Whiteman answered 9/11, 2009 at 13:56 Comment(0)
C
187

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.

Cognize answered 9/11, 2009 at 14:2 Comment(4)
I had this question too in the back of my mind. Thank you, I never thought of it in this manner.Predestine
It also helps with the "rule" of one assertion per test and blends more easily with BDD-style specifications.Iredale
And it separates the assertion mechanism from the condition (which is what leads to the better error messages).Khachaturian
The example is implausible as hardly anybody would use a single assertTrue with an &&. Separating it into two conditions makes the problem cause obvious even in JUnit. Don't get me wrong; I agree with you, I just dislike your example.Cashier
S
51

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 person who created the new syntax : here.

Seaport answered 6/4, 2010 at 14:54 Comment(0)
T
42

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.

Thordia answered 13/10, 2012 at 23:41 Comment(1)
Fest seems to have died, but the fork AssertJ is very much alive.Grayson
L
19

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.

Lithea answered 28/2, 2012 at 5:40 Comment(1)
...and when Eclipse reports an assertion failure, if you put the arguments the wrong way round in the traditional assertThat(), the error doesn't make sense.Theaterintheround
S
9

Example:

assertThat(5 , allOf(greaterThan(1),lessThan(3)));
//  java.lang.AssertionError:
//  Expected: (a value greater than <1> and a value less than <3>)
//       got: <5>
assertTrue("Number not between 1 and 3!", 1 < 5 && 5 < 3);
//  java.lang.AssertionError: Number not between 1 and 3!
  1. you can make your tests more particular
  2. you get a more detailed Exception, if tests fail
  3. easier to read the Test

btw: you can write Text in assertXXX too...

Shaunda answered 31/10, 2011 at 16:5 Comment(2)
Better yet, I would leave out the string argument in the assertThat case, because the message you get automatically is just as informative: "Expected: (a value greater than <1> and a value less than <3>)"Orlando
Yes, you are right. I edit my answer. Originially I want to use both(Matcher).and(Matcher) but it didn't work.Shaunda
J
3
assertThat(frodo.getName()).isEqualTo("Frodo");

Is close to natural language.

Easier read, easier analyze code. Programer spend more time to analyze code than write new one. So if code will be easy to analyze then developer should be more productive.

P.S. Code should be as well-written book. Self documented code.

Jankowski answered 31/1, 2015 at 23:45 Comment(1)
OK, and…? I recommend supporting your argument by explaining why that's a good thing.Rentroll
R
0

there are advantages to assertThat over assertEquals -
1) more readable
2) more information on failure
3) compile time errors - rather than run time errors
4) flexibility with writing test conditions
5) portable - if you are using hamcrest - you can use jUnit or TestNG as the underlying framework.

Redfield answered 24/9, 2017 at 6:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.