Testing equals() and hashCode()

When you working with Java Beans you almost certainly overwrite the equals(), hashCode() methods. But how to test them? The equals contract which is described in the JavaDoc of the Object class is not trivial. An even more detailed description can be found in the Effective Java book by Josuhua Bloch pp. 33-50, 1. Writing a test which includes requirements defined by the contract would not be easy. Luckily there is a tool, the EqualsVerifier written by Jan Ouwens which includes all the necessary tests. Using the EqualsVerifier is quite simple. Add the dependency to your project and add a test like to following one to a test suite:

@Test
public void verifyExample() {
    EqualsVerifier
        .forClass(Example.class)
        .verify();
}

While this would work some a limited number of entities it becomes quite cumbersome when you have more than a couple of entities. In that case the Parameterized test runner provided by JUnit helps. For a project containing the beans Person and Organization the test would look like this (package declaration and imports omitted):

@RunWith(Parameterized.class)
public class EqualsAndHashCodeTest {

    private final transient Class<?> bean;

    @Parameterized.Parameters(name = "{0}")
    public static Collection<Class<?>> data() {
        return Arrays.asList(new Class<?>[] {
            Person.class,
            Organization.class
        });
    }

    public EqualsAndHashCodeTest(final Class<?> bean) {
        this.bean = bean;
    }

    @Test
    public void verifyEqualsAndHashCode() {
        EqualsVerifier
            .forClass(bean)
            .suppress(Warning.STRICT_INHERITANCE)
            .suppress(Warning.NONFINAL_FIELDS)
            .withRedefinedSuperclass()
            .verify();
    }

The main difference to ordinary JUnit test classes is that the class does not have an parameterless constructor. Instead there is constructor which takes a Class object as parameter and puts it into a meber variable. The test class itself is annotated with the @RunWith annotation to cause JUnit the run the test with the Parameterized 2,3 test runner. The classes to test are provided by the static data method. This class simply returns a collection with the parameters for the parametrised test. In this the classes to test. The method must be annotated with the Parameterized.Parameters annotation to let JUnit know that the method provides the parameters for the parametrised test. The name parameter is used to add the name of the class to the output of JUnit.

Complete sources are available on GitHub.

References