Assert Property Equality

When you start doing more thorough testing of your code you will quickly find that the existing JUnit functionatly is not always robust enough to relieve some of the tedium in testing. Let’s take for instance a simple equals method for a person object.


public boolean equals(Object o) {
   Person person = (Person) o;
   Boolean isEqual = true;

   isEqual &= (firstName == null && person.firstName == null) || (firstName != null && person.firstName != null && firstName.equals(person.firstName);

   isEqual &= (lastName == null && person. lastName == null) || (lastName != null && person. lastName != null && lastName.equals(person. lastName);

   return isEqual;
}

Moving past the crazy Java code necessary to handle all the edge cases for equality between two objects, you will begin to notice all the edge cases you need to test. Here are the ones I noticed:

  • Same Object is Equal to Itself
  • Different Object Instances with Same Values are Equal to Each Other
  • If (insert property here) is null it is equal
  • If (insert property here) is null on one object but not the other it is not equal
  • If (insert property here) is not null but different than another object it is not equal

As you can see there is a minimum of 5 test cases for equals, and for each property you add, there is 3 more tests that need to be written. After copying and pasting a lot of code today, I decided to come up with a more reusable method. (Thank you Kent Beck…Red Green Refactor!)

So I came up with a little test Utility


package com.danielroop.test

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.lang.reflect.Method;

public class TestUtil
{
   public static void assertPropertyEquality(Class c, String property, Object value1, Object value2) throws Exception
   {
      assertPropertyNotEqualWithDifferentValues(c, property, value1, value2);
      assertPropertyWithOneNullIsNotEqual(c, property, value1);
      assertPropertyWithOneNullIsNotEqual(c, property, value2);
      assertPropertyWithBothNullsIsEqual(c, property, value1.getClass());
      assertPropertyWithBothNullsIsEqual(c, property, value2.getClass());
   }
   
   public static void assertPropertyWithBothNullsIsEqual(Class c, String property, Class type) throws Exception
   {
      Object instance1 = c.newInstance();
      Object instance2 = c.newInstance();
      
      String propertyName = property.substring(0, 1).toUpperCase();
      propertyName += property.substring(1, property.length());
      
      Method setter = c.getDeclaredMethod("set" + propertyName, type);
      
      Object[] args = new Object[1];
      args[0] = null;
      
      setter.invoke(instance1, args);
      setter.invoke(instance2, args);
      
      assertTrue(instance1.equals(instance2));
      assertTrue(instance2.equals(instance1));
   }
   
   public static void assertPropertyWithOneNullIsNotEqual(Class c, String property, Object value) throws Exception
   {
      Object instance1 = c.newInstance();
      Object instance2 = c.newInstance();
      
      String propertyName = property.substring(0, 1).toUpperCase();
      propertyName += property.substring(1, property.length());
      
      Method setter = c.getDeclaredMethod("set" + propertyName, value.getClass());
      
      Object[] args = new Object[1];
      args[0] = null;
      
      setter.invoke(instance1, value);
      setter.invoke(instance2, args);
      
      assertFalse(instance1.equals(instance2));
      assertFalse(instance2.equals(instance1));
   }
   
   
   public static void assertPropertyNotEqualWithDifferentValues(Class c, String property, Object value1, Object value2) throws Exception
   {
      Object instance1 = c.newInstance();
      Object instance2 = c.newInstance();
      
      String propertyName = property.substring(0, 1).toUpperCase();
      propertyName += property.substring(1, property.length());
      
      Method setter = c.getDeclaredMethod("set" + propertyName, value1.getClass());
      
      setter.invoke(instance1, value1);
      setter.invoke(instance2, value2);
      
      assertFalse(instance1.equals(instance2));
      assertFalse(instance2.equals(instance1));
   }
}

It isn’t the cleanest code, but it does handle the property equality cases. So to implement this, you would just staticly import the assertPropertyEquality method (or call it using the static reference TestUtil.assertPropetyEquality). What I ended up doing in my tests, is setting aside a single method for testing property equality like so.


@Test
public void testPropertyEquality() throws Exception
{
   assertPropertyEquality(Person.class, "firstName", "Daniel", "Tyler");
   assertPropertyEquality(Person.class, "lastName", "Roop", "Hunt");
}

The first paramenter is the object that should have the property set. The secod paramenter is the java bean property that should be set. The thrid and fourth parameter are two different valid values for the property.

I am not sure if this is the best way to solve this problem, but it seemed to reduce my copy/paste code significatly.

This entry was posted in programming, testing and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *