1. Overview

Hamcrest is a library that provides methods, called matchers, to help developers write simpler unit tests. There are plenty of matchers, you can get started by reading about some of them here.

In this article, we'll explore beans matchers.

2. Setup

To get Hamcrest, we just need to add the following Maven dependency to our pom.xml:

The latest Hamcrest version can be found on Maven Central.

3. Bean Matchers

Bean matchers are extremely useful to check conditions over POJOs, something that is frequently required when writing most unit tests.

Before getting started, we'll create a class that will help us through the examples:

public class City {
    String name;
    String state;

    // standard constructor, getters and setters

}

Now that we're all set, let's see beans matchers in action!

3.1. hasProperty

This matcher is basically to check if certain bean contains a specific property identified by the property's name*:*

@Test
public void givenACity_whenHasProperty_thenCorrect() {
    City city = new City("San Francisco", "CA");
    
    assertThat(city, hasProperty("state"));
}

So, this test will pass because our City bean has a property named state.

Following this idea, we can also test if a bean has certain property and that property has certain value:

@Test
public void givenACity_whenHasPropertyWithValueEqualTo_thenCorrect() {
    City city = new City("San Francisco", "CA");
        
    assertThat(city, hasProperty("name", equalTo("San Francisco")));
}

As we can see, hasProperty is overloaded and can be used with a second matcher to check a specific condition over a property.

So, we can also do this:

@Test
public void givenACity_whenHasPropertyWithValueEqualToIgnoringCase_thenCorrect() {
    City city = new City("San Francisco", "CA");

    assertThat(city, hasProperty("state", equalToIgnoringCase("ca")));
}

Useful, right? We can take this idea one step further with the matcher that we'll explore next.

3.2. samePropertyValuesAs

Sometimes when we have to do checks over a lot of properties of a bean, it may be simpler to create a new bean with the desired values. Then, we can check for equality between the tested bean and the new one. Of course, Hamcrest provides a matcher for this situation:

@Test
public void givenACity_whenSamePropertyValuesAs_thenCorrect() {
    City city = new City("San Francisco", "CA");
    City city2 = new City("San Francisco", "CA");

    assertThat(city, samePropertyValuesAs(city2));
}

This results in fewer assertions and simpler code. Same way, we can test the negative case:

@Test
public void givenACity_whenNotSamePropertyValuesAs_thenCorrect() {
    City city = new City("San Francisco", "CA");
    City city2 = new City("Los Angeles", "CA");

    assertThat(city, not(samePropertyValuesAs(city2)));
}

Next, well see a couple of util methods to inspect class properties.

3.3. getPropertyDescriptor

There're scenarios when it may come in handy being able to explore a class structure. Hamcrest provides some util methods to do so:

@Test
public void givenACity_whenGetPropertyDescriptor_thenCorrect() {
    City city = new City("San Francisco", "CA");
    PropertyDescriptor descriptor = getPropertyDescriptor("state", city);

    assertThat(descriptor
      .getReadMethod()
      .getName(), is(equalTo("getState")));
}

The object descriptor retrieves a lot of information about the property state. In this case, we've extracted the getter's name and assert that it is equal to some expected value. Note that we can also apply other text matchers.

Moving on, the last method we will explore is a more general case of this same idea.

3.4. propertyDescriptorsFor

This method does basically the same as the one in the previous section, but for all the properties of the bean. We also need to specify how high we want to go in the class hierarchy:

@Test
public void givenACity_whenGetPropertyDescriptorsFor_thenCorrect() {
    City city = new City("San Francisco", "CA");
    PropertyDescriptor[] descriptors = propertyDescriptorsFor(
      city, Object.class);
 
    List<String> getters = Arrays.stream(descriptors)
      .map(x -> x.getReadMethod().getName())
      .collect(toList());

    assertThat(getters, containsInAnyOrder("getName", "getState"));
}

So, what we did here is: get all the property descriptors from the bean city and stop at the Object level.

Then, we just used Java 8's features to filter the getter methods.

Finally, we used collections matchers to check something over the getters list. You can find more information about collections matchers here.

4. Conclusion

Hamcrest matchers consist of a great set of tools to be used across every project. They're easy to learn and become extremely useful in short time.

Beans matchers in particular, provide an effective way of making assertions over POJOs, something that is frequently required when writing unit tests.

To get the complete implementation of this examples, please refer to the GitHub project.