HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Unit testing Equals, hashcode and comparator - asserting contracts

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
contractsassertinghashcodetestingequalsandcomparatorunit

Problem

After reading Joshua Blosh's Effective Java 2nd edition, I decided to implement equals, hashcode and comparable(where applicable) to every class I have been implementing.

I have written a generic unit test class for equals, hashcode and comparable that will test every contract of each function.

I've attached the generic tester class for anyone who would be interested in seeing them.

My question is, am I overkilling the testing? Have I missed any clause in the equals, hashcode and compareTo contract?
These is a tester class I've written once, and used numerous times. However, working on my own, I have not had the chance to get a second opinion with fellow developers.

My class EqualsContractTester

```
public class EqualsContractTester
{
/**
* according to Object.equals
*for any non-null reference value x, x.equals(x) should return true.
* @param o
*/
public static void testReflexive(Object o)
{
assertEquals("Object o should be reflexibly equal to itself.", o, o);
}
/**
* for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
* @param o1
* @param o2
*/
public static void testSymmetric(Object o1, Object o2)
{
assertEquals("o1 and o2 should be symetrically equal to each other.", o1, o2);

assertEquals("o2 and o1 should be symetrically equal to each other.",o2, o1);
}
/**
* for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
* @param o1
* @param o2
* @param o3
*/
public static void testTransitive(Object o1, Object o2, Object o3)
{
assertEquals("o1 should transitively be equal to o2.", o1, o2);

assertEquals("o2 should transitively be equal to o3.", o2, o3);

assertEquals("o1 should transitively be equal to o3.", o1, o3);
}
/**
* For any non-null refer

Solution

This is a nice idea and those classes can indeed be reused on many occasions, unless equals and hashCode are generated with utils like lombok.

Here are some suggestions of how this code can be improved.

General Design

Currently, these classes contain lots of public static methods, which means that calls like ComparableTester.assertConsistencyWithEqual(o, o) look rather ugly and are too verbose in usage.

But these entities can become more flexible and easily pluggable if transformed into abstract classes or interfaces. For example:

public interface EqualsContractTester {

  public default void assertEqualsReflexive(Object o) {
    // impl...
  }

}


And if you add an abstact method like

@Test
public void testEqualsReflexive();


this already begins to look like a testing framework. Now, each test class that plugs the interface will have to implement the test method.

Moreover, using generic types (e.g. EqualsContractTester) will allow to avoid to have Object args in methods, but this is optional.

Naming and Style

Method Names

The methods prefixed with test should be renamed to assert because assertions are what they actually do. The test* prefix is often used for test cases annotated with @Test.

Braces

Opening braces on new lines for code blocks are not Java-like style, so format them properly :)

Other

Expected Exceptions

In assertNullPointerException, instead of using a try-catch block, it is more concise to use @Test(expected = NullPointerException.class) on the test method that invokes it. The fail() call will neither be necessary in this case.

Generics

It's probably a matter of taste, but a method signature

public static  void assertComparisonReversal(Comparable o1, Comparable o2);


looks more readable for me than

public static > void assertComparisonReversal(T o1, T o2)

Code Snippets

public interface EqualsContractTester {

  public default void assertEqualsReflexive(Object o) {
    // impl...
  }

}
@Test
public void testEqualsReflexive();
public static <T> void assertComparisonReversal(Comparable<T> o1, Comparable<T> o2);
public static <T extends Comparable<T>> void assertComparisonReversal(T o1, T o2)

Context

StackExchange Code Review Q#129358, answer score: 3

Revisions (0)

No revisions yet.