Write More Declaritive JUnit Assertions with Hamcrest

Posted On // 2 comments
The Hamcrest framework is a generic "matching" framework that JUnit added direct support for with version 4.4. Hamcrest allows developers to write more declarative assertions by using both built in matchers, as well as a clean way to define your own matchers.

The online documentation on Hamcrest is mostly limited to some API docs, test cases, and a brief online tutorial. However the API is fairly intuitive for basic checks. One area that was intersting to me was the collection portion of the API.

Hamcrest allows you to perform some assertions on collections to check for the existence of an item in the collection, but all the examples I saw were based on simple collections of items that implemented "equals" (Strings, primitives, etc). However, I wanted to find a way to validate more complex objects that may not have implemented equals. After some experimentation, I found I nice way to do what I was trying do.

Basically, the collection matchers allow for embedded matchers to be used. So I started off by writing something like the following:

//This set _should_ actually be a set with some data you want to test in it.
//This is just an example...
Set>MyCustomBean< set = new HashSet>MyCustomBean<();

assertThat(set, hasItem(hasProperty("propertyToTest", equalTo("valueToFind"))));

The problem with the above code is that Eclipse kept complaining with the following error:

The method assertThat(T, Matcher>T<) in the type Assert is not applicable for the arguments (Set>MyCustomBean<, Matcher>Iterable>Object<<)

I haven't totally figured out exactly why this problem is occurring yet, but it seems that the hasProperty matcher is binding a type parameter as Object, and that is messing with the outer assertThat call. To fix it, I came up with two ways to make it work.

You can either rebuild the set as the following:

assertThat(new HashSet>Object<(set), hasItem(hasProperty("propertyToTest", equalTo("valueToFind"))));

Or you can take the more efficient approach that drops a little of the syntactic sugar of Hamcrest:

assertThat(set, hasItem(new HasPropertyWithValue("propertyToTest", equalTo("valueToFind"))));

I'm not totally happy with either approach but I do like the fact that I can have a mostly understandable one-liner that checks that a collection has a bean in it that has a property with a certain value.

I'm going to keep looking for a better solution, but can anyone suggest a better way to make this work and still keep the nice Hamcrest sugar intact?


James Williams said...

Very nice. I like option #2 better but I agree that there should be a better way to do this. Maybe the Hamcrest guys should add this type of check as part of the framework.

Brian said...

Even though this is a year after the original post, there still isn't any proper documentation or examples on how to do this :\

A 3rd way to do this is to apply a type to your matcher like this:

Set<String> set = someSet;

assertThat(set, hasItem(Matchers.<String>hasProperty("propertyName", equalTo("value"))));

This works, and better yet - no complaints about unchecked assignments :)