Just recently I had to implement equals() and hashCode() for several classes. With the aid of your favourite IDE, generating these methods is a matter of seconds.

Well, if there weren’t the missing tests. As I tend to be obsessive when it comes to tests, I also want to have tests for equals() and hashCode(). Only then I can refactor the generated code to align it with the project’s code style and make sure not to break it thereby.

After being half-way through writing tests for the second class that implemented equals() and hashCode(), and more and more copying code from the existing tests and getting more and more impatient with the progress I made, I decided to write a helper class that would hopefully reduce the redundant test code.

A few iterations later I had an EqualsTester class. The general idea is to feed its assert-methods with probes that are known to be equal or unequal. They will then ensure that equals() and hashCode() work like specified as far as possible. If testing a probe fails, an AssertionError will be thrown. This is best illustrated with a code snippet:

@Test public void testEqualsAndHashCode() { EqualsTester<Point> equalsTester = newInstance( new Point( 1, 2 ) ); equalsTester.assertEqual( new Point( 1, 2 ), new Point( 1, 2 ) ); equalsTester.assertNotEqual( new Point( 1, 2 ), new Point( 3, 4 ) ); }

The factory method is just there to avoid yet another pair of angle brackets. With the given argument, a number of tests are executed to ensure that the argument

is equal with itself ( object.equals( object ) == true )

) is not equal with null

is not equal with new Object()

By passing two objects that are considered equal to assertEqual() , you can not only ensure that equals() is implemented correctly but also that their hash code is the same.

assertNotEqual() makes sure that your equals() implementation does not accidentally consider unequal objects as equal. By default assertNotEqual() will also check that hashCode() produces distinct results. This may improve the performance of hash tables. As the general contract doesn’t require it, it can, of course, be disabled.

This utility class helped me, thus, I thought it would be worth sharing. I hope that one or another will also find it useful.