Hamcrest is a useful library that lets you build assertions declaratively. Having worked with the library for a while now, I've found a few useful patterns and a handful of gotchas when writing Hamcrest matchers.
Make matchers granular
Sometimes you want to compare features of two objects having the same type. Instead of making one matcher that compares many features, write many matchers, each of which compares one feature. By doing this, you can use your custom matchers with Hamcrest's built-in combining matchers. FeatureMatchers work well for this type of fine-grained matcher. If the objects's features are bean properties you can avoid writing a custom matcher by using Hamcrest's hasProperty().
You get more power by allowing matchers to accept other matchers. Here's an example that combines Hamcrest matchers to make an assertion on a JAX-RS Response:
[Aside: Due to a bug in 1.3RC2, you will need to use a more recent version to get useful diagnostics from
Prefer TypeSafeDiagnosingMatchers over TypeSafeMatchers
Unless your matchers are dealing with simple types such as Strings or primitives, TypeSafeDiagnosingMatchers will be able to produce better diagnostics. If you're seeing diagnostics such as
Expected: status <404> but: was <com.sun.jersey.core.spi.factory.ResponseImpl@575b2f17>, you probably need a diagnosing matcher.
Avoid null checks inside Matcher code
TypeSafeMatcher and TypeSafeDiagnosingMatchers won't even call your matcher code if the actual value is null. If you're expecting a null value, use
Matchers.nullValue() instead of a custom matcher.
Avoid using mutable objects as actual or expected items
Hamcrest assumes that
Matcher.matches() is idempotent; indeed,
TypeSafeDiagnosingMatcher.describeMismatch() gets called twice when two objects do not match. This means that you should avoid writing an InputStream matcher, for example.