Appendix B. Writing a Hamcrest Matcher

Introduction

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:

image

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).

A New Matcher Type

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.

image

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.

image

Matcher Objects Must Be Stateless

image

When dispatching each invocation, jMock uses the matchers to find an expectation that matches the invocation’s arguments. This means that it will call the matchers many times during the test, maybe even after the expectation has already been matched and invoked. In fact, jMock gives no guarantees of when and how many times it will call the matchers. This has no effect on stateless matchers, but the behavior of stateful matchers is unpredictable.

If you want to maintain state in response to invocations, write a custom jMock Action, not a Matcher.

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.

image

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.231.15