Although Hamcrest 1.2 comes with a large library of matchers, sometimes these do not let you specify an assertion or expectation accurately enough to convey what you mean or to keep your tests flexible. In such cases, you can easily define a new matcher that seamlessly extends the JUnit and jMock APIs.
A matcher is an object that implements the org.hamcrest.Matcher
interface:
A matcher does two things:
• Reports whether a parameter value meets the constraint (the matches()
method);
• Generates a readable description to be included in test failure messages (the describeTo()
method inherited from the SelfDescribing
interface and the describeMismatch()
method).
As an example, we will write a new matcher that tests whether a string starts with a given prefix. It can be used in tests as shown below. Note that the matcher seamlessly extends the assertion: there is no visible difference between built-in and third-party matchers at the point of use.
To write a new matcher, we must implement two things: a new class that implements the Matcher
interface and the startsWith()
factory function for our assertions to read well when we use the new matcher in our tests.
To write a matcher type, we extend one of Hamcrest’s abstract base classes, rather than implementing the Matcher
interface directly.1 For our needs, we can extend TypeSafeMatcher<String>
, which checks for nulls and type safety, casts the matched Object
to a String
, and calls the template methods [Gamma94] in our subclass.
1. This lets the Hamcrest team add methods to the Matcher
interface without breaking all the code that implements that interface, because they can also add a default implementation to the base class.
The text generated by the describeTo()
and describeMismatch()
must follow certain grammatical conventions to fit into the error messages generated by JUnit and jMock. Although JUnit and jMock generate different messages, matcher descriptions that complete the sentence “expected description
but it mismatch-description
” will work with both libraries. That sentence completed with the StringStartsWithMatcher
’s descriptions would be something like:
expected a string starting with "Cheese" but it started with "Bananas"
To make the new matcher fit seamlessly into JUnit and jMock, we also write a factory method that creates an instance of the StringStartsWithMatcher
.
The point of the factory method is to make the test code read clearly, so consider how it will look when used in an assertion or expectation.
And that’s all there is to writing a matcher.
18.117.231.15