EqualityTestCase: Difference between revisions

From EggeWiki
mNo edit summary
mNo edit summary
Line 1: Line 1:
<geshi lang="java5">
<geshi lang="java5">
import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayOutputStream;
Line 8: Line 9:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Iterator;
Line 71: Line 72:
  *  
  *  
  * If the object under test implements Comparable then a < b (< c)
  * If the object under test implements Comparable then a < b (< c)
*
* @author brianegge
  */
  */
public abstract class EqualityTestCase extends TestCase {
public abstract class EqualityTestCase<T> extends TestCase {


/**
/**
Line 81: Line 80:
*        created by getA, but be not equals to objects created by getB
*        created by getA, but be not equals to objects created by getB
*/
*/
protected abstract Object getA();
protected abstract T getA() throws Exception;


/**
/**
Line 88: Line 87:
*        created by getA, but be not equals to objects created by getB
*        created by getA, but be not equals to objects created by getB
*/
*/
protected abstract Object getB();
protected abstract T getB() throws Exception;


/**
/**
Line 94: Line 93:
*        optionally be implemented.
*        optionally be implemented.
*/
*/
protected Object getC() {
protected T getC() throws Exception {
return null;
return null;
}
}


public void testEquals() {
public void testEquals() throws Exception {
assertFalse(getA().equals(null));
assertFalse(getA().equals(null));
assertFalse(getB().equals(null));
assertFalse(getB().equals(null));
Line 118: Line 117:
}
}
for (Iterator iterator = getObjects().iterator(); iterator.hasNext();) {
for (Iterator<T> iterator = getObjects().iterator(); iterator.hasNext();) {
Object o = iterator.next();
Object o = iterator.next();
// The equals method is reflexive, which means that an object is equal to itself: x.equals(x) should return true.
// The equals method is reflexive, which means that an object is equal to itself: x.equals(x) should return true.
Line 127: Line 126:
}
}


public void testHashCode() {
public void testHashCode() throws Exception {
assertEquals("hashCodes differ for two A objects", getA().hashCode(), getA().hashCode());
assertEquals("hashCodes differ for two A objects", getA().hashCode(), getA().hashCode());
assertEquals(getB().hashCode(), getB().hashCode());
assertEquals(getB().hashCode(), getB().hashCode());
Line 142: Line 141:
}
}


Set set = new HashSet();
Set<T> set = new HashSet<T>();
set.add(getA());
set.add(getA());


Line 150: Line 149:
}
}


@SuppressWarnings("unchecked") // Comparable<T> is checked only at runtime
public void testComparable() throws Exception {
public void testComparable() throws Exception {
if (getA() instanceof Comparable) {
if (getA() instanceof Comparable) {
assertEquals(0, ((Comparable) getA()).compareTo(getA()));
assertEquals(0, ((Comparable<T>) getA()).compareTo(getA()));
assertEquals(0, ((Comparable) getB()).compareTo(getB()));
assertEquals(0, ((Comparable<T>) getB()).compareTo(getB()));
assertEquals("Item B should compare equally with another item B", 0, ((Comparable<T>) getB()).compareTo(getB()));


assertTrue(((Comparable) getA()).compareTo(getB()) < 0);
assertTrue("Item B should be less than Item A", ((Comparable<T>) getA()).compareTo(getB()) < 0);
assertTrue(((Comparable) getB()).compareTo(getA()) > 0);
assertTrue(((Comparable<T>) getB()).compareTo(getA()) > 0);
if (getC() != null) {
if (getC() != null) {
assertEquals(0, ((Comparable) getC()).compareTo(getC()));
assertEquals(0, ((Comparable<T>) getC()).compareTo(getC()));
assertTrue(((Comparable) getA()).compareTo(getC()) < 0);
assertTrue("Item C should be less than Item A", ((Comparable<T>) getA()).compareTo(getC()) < 0);
assertTrue(((Comparable) getB()).compareTo(getC()) < 0);
assertTrue("Item C should be less than Item B", ((Comparable<T>) getB()).compareTo(getC()) < 0);
assertTrue(((Comparable) getC()).compareTo(getB()) > 0);
assertTrue(((Comparable<T>) getC()).compareTo(getB()) > 0);
}
}
}
}
Line 168: Line 169:
public void testSerializable() throws Exception {
public void testSerializable() throws Exception {
if (getA() instanceof Serializable) {
if (getA() instanceof Serializable) {
for (int i = 0; i < objects().length; i++) {
for (T o : getObjects()) {
Object o = objects()[i];
assertTrue(o.equals(serialize(o)));
assertEquals(o, serialize(o));
}
}
}
}
Line 197: Line 197:
* @throws IllegalArgumentException
* @throws IllegalArgumentException
*/
*/
public void testClone() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
public void testClone() throws Exception,
IllegalAccessException, InvocationTargetException {
IllegalAccessException, InvocationTargetException {
if (getA() instanceof Cloneable) {
if (getA() instanceof Cloneable) {
for (int i = 0; i < objects().length; i++) {
for (T o : getObjects()) {
Object o = objects()[i];
Method clone = o.getClass().getMethod("clone", new Class[] {});
Method clone = o.getClass().getMethod("clone", new Class[] {});
Object cloned = clone.invoke(o, new Object[] {});
Object cloned = clone.invoke(o, new Object[] {});
Line 215: Line 214:
* this is a 'good' toString, other than to make sure the code works and doesn't return <code>null</code>.
* this is a 'good' toString, other than to make sure the code works and doesn't return <code>null</code>.
*/
*/
public void testToString() {
public void testToString() throws Exception {
for (Iterator iterator = getObjects().iterator(); iterator.hasNext();) {
for (Iterator<T> iterator = getObjects().iterator(); iterator.hasNext();) {
Object o = iterator.next();
Object o = iterator.next();
assertNotNull(o.toString());
assertNotNull(o.toString());
Line 222: Line 221:
}
}


/**
/**
* This will either return an array of one or two objects depending on if getC() is implemented.
* This will either return a list of one or two objects depending on if getC() is implemented.
* @return a list of test objects.   
* @return a list of test objects.   
*/
*/
private Object[] objects() {
private List<T> getObjects() throws Exception {
List<T> list = new ArrayList<T>(3);
list.add(getA());
list.add(getB());
if (getC() != null) {
if (getC() != null) {
return new Object[] { getA(), getB(), getC() };
list.add(getC());
}
}
return new Object[] { getA(), getB() };
return list;
}
private List getObjects() {
return Arrays.asList(objects());
}
}
}
}

Revision as of 23:55, 5 August 2009

<geshi lang="java5">

import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set;

import junit.framework.TestCase;

/**

* This base class is for testing that an object properly overrides
* {@link Object#equals} and {@link Object#hashCode}.
* 

*

* Here are the important contract requirements for the two methods, as * documented in the javadoc documentation for java.lang.Object: *

*

    * *
  1. The hashCode method must return the same integer value * every time it is invoked on the same object during the entire execution of a * Java application or applet. It need not return the same value for different * runs of an application or applet. The Java 2 platform (Java 2) documentation * further allows the hashCode value to change if the information * used in the equals method changes.
    *
    *
  2. *
  3. If two objects are equal according to the equals method, * they must return the same value from hashCode.
    *
    *
  4. * *
  5. The equals method is reflexive, which means that an * object is equal to itself: x.equals(x) should return true.
    *
    *
  6. *
  7. The equals method is symmetric: If * x.equals(y) returns true, then y.equals(x) * should return true also.
    *
    *
  8. * *
  9. The equals method is transitive: If * x.equals(y) returns true and y.equals(z) * returns true, then x.equals(z) should return true.
    *
    *
  10. *
  11. The equals method is consistent. x.equals(y) * should consistently return either true or false. The Java 2 javadoc clarifies * that the result of x.equals(y) can change if the information * used in the equals comparisons change.
    *
    *
  12. * *
  13. Finally, x.equals(null) should return false.
  14. *
* 
* Additionally, if this class implements {@link Comparable},
* {@link Serializable}, or {@link Cloneable}, then the object will be checked
* to see if these methods provide reasonable behavior.
* 
* You must implement two factory methods: getA, getB.
* 
* If the object under test implements Comparable then a < b (< c)
*/

public abstract class EqualityTestCase<T> extends TestCase {

/** * * @return a new instance of an Object which should equate to other objects * created by getA, but be not equals to objects created by getB */ protected abstract T getA() throws Exception;

/** * * @return a new instance of an Object which should equate to other objects * created by getA, but be not equals to objects created by getB */ protected abstract T getB() throws Exception;

/** * @return an object which is not equal to either A or B. This method can * optionally be implemented. */ protected T getC() throws Exception { return null; }

public void testEquals() throws Exception { assertFalse(getA().equals(null)); assertFalse(getB().equals(null)); assertFalse(getA().equals(new Object())); assertFalse(getB().equals(new Object())); //The equals method is symmetric: If x.equals(y) returns true, then y.equals(x) should return true also. assertTrue(getA().equals(getA())); assertTrue(getB().equals(getB())); assertFalse(getA().equals(getB())); assertFalse(getB().equals(getA())); if (getC() != null) { assertFalse(getC().equals(null)); assertFalse(getC().equals(new Object())); assertTrue(getC().equals(getC())); assertFalse(getA().equals(getC())); assertFalse(getC().equals(getA())); assertFalse(getB().equals(getC())); assertFalse(getC().equals(getB())); }

for (Iterator<T> iterator = getObjects().iterator(); iterator.hasNext();) { Object o = iterator.next(); // The equals method is reflexive, which means that an object is equal to itself: x.equals(x) should return true. assertTrue(o.equals(o)); // x.equals(null) should return false. assertFalse(o.equals(null)); } }

public void testHashCode() throws Exception { assertEquals("hashCodes differ for two A objects", getA().hashCode(), getA().hashCode()); assertEquals(getB().hashCode(), getB().hashCode()); assertFalse("hashCode is either not implmented correctly or is very bad\n" + "Both A and B returned a hashCode of " + getA().hashCode(), getA().hashCode() == getB().hashCode()); if (getC() != null) { assertEquals(getC().hashCode(), getC().hashCode()); assertFalse("hashCode is either not implmented correctly or is very bad\n" + "Both A and C returned a hashCode of " + getA().hashCode(), getA().hashCode() == getC() .hashCode()); assertFalse("hashCode is either not implmented correctly or is very bad\n" + "Both B and C returned a hashCode of " + getB().hashCode(), getB().hashCode() == getC() .hashCode()); }

Set<T> set = new HashSet<T>(); set.add(getA());

assertTrue(set.contains(getA())); assertFalse(set.contains(getB())); assertFalse(set.contains(getC())); }

@SuppressWarnings("unchecked") // Comparable<T> is checked only at runtime public void testComparable() throws Exception { if (getA() instanceof Comparable) { assertEquals(0, ((Comparable<T>) getA()).compareTo(getA())); assertEquals(0, ((Comparable<T>) getB()).compareTo(getB())); assertEquals("Item B should compare equally with another item B", 0, ((Comparable<T>) getB()).compareTo(getB()));

assertTrue("Item B should be less than Item A", ((Comparable<T>) getA()).compareTo(getB()) < 0); assertTrue(((Comparable<T>) getB()).compareTo(getA()) > 0); if (getC() != null) { assertEquals(0, ((Comparable<T>) getC()).compareTo(getC())); assertTrue("Item C should be less than Item A", ((Comparable<T>) getA()).compareTo(getC()) < 0); assertTrue("Item C should be less than Item B", ((Comparable<T>) getB()).compareTo(getC()) < 0); assertTrue(((Comparable<T>) getC()).compareTo(getB()) > 0); } } }

public void testSerializable() throws Exception { if (getA() instanceof Serializable) { for (T o : getObjects()) { assertTrue(o.equals(serialize(o))); } } }

private Object serialize(Object value) throws IOException, ClassNotFoundException { ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); ObjectOutputStream outputStream = new ObjectOutputStream(outputBuffer); outputStream.writeObject(value); outputStream.flush(); byte[] bytes = outputBuffer.toByteArray(); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); return objectInputStream.readObject(); }

/** * If you must implement {@link java.lang.Cloneable}, please do it correctly! The clone interface doesn't require * you to make 'clone' a public method, but this what one generally expects for an object which * implements {@link java.lang.Cloneable} * * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException */ public void testClone() throws Exception, IllegalAccessException, InvocationTargetException { if (getA() instanceof Cloneable) { for (T o : getObjects()) { Method clone = o.getClass().getMethod("clone", new Class[] {}); Object cloned = clone.invoke(o, new Object[] {}); assertNotSame(o, cloned); assertEquals(o, cloned); assertEquals(o.getClass(), cloned.getClass()); } } }

/** * toString doesn't have to be implemented. There's not a lot of checks we can do to make sure * this is a 'good' toString, other than to make sure the code works and doesn't return null. */ public void testToString() throws Exception { for (Iterator<T> iterator = getObjects().iterator(); iterator.hasNext();) { Object o = iterator.next(); assertNotNull(o.toString()); } }


/** * This will either return a list of one or two objects depending on if getC() is implemented. * @return a list of test objects. */ private List<T> getObjects() throws Exception { List<T> list = new ArrayList<T>(3); list.add(getA()); list.add(getB()); if (getC() != null) { list.add(getC()); } return list; } } </geshi>