1. Purpose of the Hamcrest matcher framework
Hamcrest is a framework for software tests. Hamcrest allows checking for conditions in your code via existing matchers classes and allows you to define your custom matcher implementations.
To use Hamcrest matchers in JUnit you use the
assertThat
statement followed by one or several matchers.
Hamcrest is typically viewed as a third generation matcher framework. The first generation used
assert(logical statement)
but such tests were not easily readable. The second generation introduced special methods for assertions, e.g., assertEquals()
but that lead to lots of assert methods. Hamcrest uses the assertThat
method with a matcher expression to determine if the test was succesfully See Wiki on Hamcrest for more details.
Hamcrest has the target to make tests as readable as possible. For example the
is
method is a thin wrapper forequalTo(value)
.import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.equalTo; boolean a; boolean b; // all statements test the same assertThat(a, equalTo(b)); assertThat(a, is(equalTo(b))); assertThat(a, is(b));
The following snippets compare pure JUnit 4 assert statements with Hamcrest matchers.
// JUnit 4 for equals check assertEquals(expected, actual); // Hamcrest for equals check assertThat(actual, is(equalTo(expected))); // JUnit 4 for not equals check assertFalse(expected.equals(actual)); // Hamcrest for not equals check assertThat(actual, is(not(equalTo(expected))));
It is also possible to chain matchers, via the
anyOf
of allOf
method.assertThat("test", anyOf(is("testing"), containsString("est")));
In general the Hamcrest error messages are also much easier to read and using Hamcrest matchers provide more type safety as these matchers uses generics.
To use the library for a Maven based project, the following dependency to your pom file.
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
To use Hamcrest matchers for a project based on the Gradle build system, add the following dependencies to it.
dependencies { // Unit testing dependencies testCompile 'junit:junit:4.12' // Set this dependency if you want to use Hamcrest matching testCompile 'org.hamcrest:hamcrest-library:1.3' }
The JUnit distribution included in Eclipse only contain the core Hamcrest matcher. To use all available matchers, download the latest hamcrest-all-*.jar from https://code.google.com/p/hamcrest/downloads/list and add it to your projects classpath.
The usage of Hamcrest matches is demonstrates by the following code snippet.
assertThat(Long.valueOf(1), instanceOf(Integer.class));
The usage of the Hamcrest matchers for lists are demonstrated by the following example.
import org.junit.Test; import java.util.Arrays; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.hamcrest.core.Every.everyItem; public class HamcrestListMatcherExamples { @Test public void listShouldInitiallyBeEmpty() { List<Integer> list = Arrays.asList(5, 2, 4); assertThat(list, hasSize(3)); // ensure the order is correct assertThat(list, contains(5, 2, 4)); assertThat(list, containsInAnyOrder(2, 4, 5)); assertThat(list, everyItem(greaterThan(1))); } }
The following list contains the most important Hamcrest matchers.
- allOf - matches if all matchers match (short circuits)
- anyOf - matches if any matchers match (short circuits)
- not - matches if the wrapped matcher doesn't match and vice versa
- equalTo - test object equality using the equals method
- is - decorator for equalTo to improve readability
- hasToString - test Object.toString
- instanceOf, isCompatibleType - test type
- notNullValue, nullValue - test for null
- sameInstance - test object identity
- hasEntry, hasKey, hasValue - test a map contains an entry, key or value
- hasItem, hasItems - test a collection contains elements
- hasItemInArray - test an array contains an element
- closeTo - test floating point values are close to a given value
- greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo - test ordering
- equalToIgnoringCase - test string equality ignoring case
- equalToIgnoringWhiteSpace - test string equality ignoring differences in runs of whitespace
- containsString, endsWith, startsWith - test string matching
It is possible to write your custom Hamcrest matcher by extending TypeSafeMatcher. The following is an example for defining a matcher which allows testing if a String matches a regular expression.
import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; public class RegexMatcher extends TypeSafeMatcher<String> { private final String regex; public RegexMatcher(final String regex) { this.regex = regex; } @Override public void describeTo(final Description description) { description.appendText("matches regular expression=`" + regex + "`"); } @Override public boolean matchesSafely(final String string) { return string.matches(regex); } // matcher method you can call on this matcher class public static RegexMatcher matchesRegex(final String regex) { return new RegexMatcher(regex); } }
The following snippet gives an example how to use it.
package com.vogella.android.testing.applicationtest; import org.junit.Test; import static org.hamcrest.MatcherAssert.assertThat; public class TestCustomMatcher { @Test public void testRegularExpressionMatcher() throws Exception { String s ="aaabbbaaaa"; assertThat(s, RegexMatcher.matchesRegex("a*b*a*")); } }
See Hamcrest tutorial for more examples.
Pretty article! I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing..
ReplyDeleteios App Development Company
Mobile App Development Company
Best Mobile App Development Company