I did the tests below with Java 8, Junit 4.12 and hamcrest-all 1.3
At first it seems to use assertThat
would not be necessary, as similar results can be obtained with the other methods assertXXX
. Examples:
// são equivalentes
assertThat("1991", is(algumValor)); // sendo que algumValor é uma String
assertEquals("1991", algumValor);
// são equivalentes
assertThat("1991", is(not("2019")));
assertNotEquals("1991", "2019");
But there are some advantages to using assertThat
. Suppose I have a list of String
and one of the tests checks whether it contains two specific elements:
List<String> list = Arrays.asList("abc", "def", "ghi");
assertTrue(list.contains("abc") && list.contains("xyz"));
Although it is not such a complicated code to understand, the version with assertThat
and the use of matcher org.hamcrest.core.IsCollectionContaining
(that has the method hasItems
) leaves the code more readable and expressive (at least for those who know English):
// é quase uma frase em inglês (verifique se a lista tem os itens "abc" e "xyz")
assertThat(list, hasItems("abc", "xyz"));
Another advantage is the error message if the test fails. While assertTrue
only gave me one AssertionError
(without any additional message, only the line that failed), the assertThat
gave this message:
java.lang.AssertionError:
Expected: (a collection containing "abc" and a collection containing "xyz")
but: a collection containing "xyz" was "abc", was "def", was "ghi"
Much more detailed, describing exactly the problem (a Collection containing "abc" and "Xyz", but she only had "abc", "def" and "ghi").
Another example:
import static org.hamcrest.core.AllOf.allOf;
import static org.hamcrest.number.OrderingComparison.greaterThan;
import static org.hamcrest.number.OrderingComparison.lessThan;
int x = 5;
assertThat(x, allOf(greaterThan(1), lessThan(3)));
allOf
receives a list of matchers and check if all are valid. In case, I used greaterThan
and lessThan
, that is, I check if the number is greater than 1 and less than 3. How I tested the value 5
, the result is a AssertionError
with the message:
java.lang.AssertionError:
Expected: (a value greater than <1> and a value less than <3>)
but: a value less than <3> <5> was greater than <3>
I could do too:
int x = 5;
assertTrue(x > 1 && x < 3);
But again, the AssertionError
does not give me a descriptive message, only the line that gave the error. And the version with assertThat
and the matchers makes the code closer to natural language (or more "expressive", "readable", etc.). Of course this depends on the opinion of each one, but I think it is positive that you can choose the option you think best.
Besides being able to be chained and combined, as shown in the examples above, there is still the possibility of create your own matcher, where some more complex and/or specific validation is required, for example.
To documentation suggests that a subclass of org.hamcrest.TypeSafeMatcher
, which checks that the value being tested is not null and makes the cast for its type. Example:
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class MyMatcher extends TypeSafeMatcher<String> {
@Override
protected boolean matchesSafely(String item) {
// aqui pode ter o critério que você quiser
// retorna true ou false, indicando se a String é válida ou não
}
@Override
public void describeTo(Description description) {
// coloque uma mensagem bem descritiva aqui
description.appendText("não passou nos critérios");
}
public static MyMatcher criterioXyz() {
return new MyMatcher();
}
}
And to use it:
import static br.tests.MyMatcher.criterioXyz;
assertThat("string", criterioXyz());
Obviously criterioXyz()
should be replaced by a more significant name indicating what the matcher is checking out.
Of course it is possible to do the same check using the other methods assertXXX
. It is up to you to decide whether the extra complexity of the matchers is a fair price to pay to increase the readability of the code, in addition to having more descriptive error messages and the possibility to create your own matchers (this last item I confess I never had to use, but you may need it and think it’s worth using).