Should I test hashCode(), equals(), toString()?

The Java language provides some default functions on objects that we usually want to override:

toString()
equals()
hashCode()

This is plumbing; the methods can’t fulfil any business requirements in themselves, but Java developers generally put them into objects early on. In a way this is a violation of YAGNI, which goes against the TDD grain.

Watching James Shore’s test driven development videos, I came across an interesting approach to implementing these methods. James begins with a single test, valueObject(), to check these functions:

@Test
public void valueObject() {
  Year year1a = new Year(2010);
  Year year1b = new Year(2010);
  Year year2 = new Year(2012);
   
  assertEquals("2010", year1a.toString());
  assertTrue("years with same value should be equal", year1a.equals(year1b));
  assertFalse("years with different values should not be equal", year1a.equals(year2));
  assertTrue("years with same value should have same hashcode", year1a.hashCode() == year1b.hashCode());
}

He then makes the tests compile by implementing the class.

package com.jamesshore.finances.domain;

public class Year {
  int year;
}

And then uses the Eclipse tool for autogenerating equals, hashCode and toString:

public String toString() {
  return "" + year;
}

@Override
public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + year;
  return result;
}

@Override
public boolean equals(Object obj) {
  if (this == obj) return true;
  if (obj == null) return false;
  if (getClass() != obj.getClass()) return false;
  Year other = (Year) obj;
  if (year != other.year) return false;
  return true;
}

Now the tests pass.  Its interesting how the boilerplate code of the hashCode() etc. requires boilerplate code in the test.  Though it violated YAGNI, most Java developers I know prefer to create these methods up front, and this seems an easy way to handle it.

These methods are generally only needed on value object (i.e. objects where equality is important) or entity objects (where identity is important).  Developers generally know when they create classes whether they are value or entity objects, so it makes sense to me to create these methods up front.