EqualityTestCase: Difference between revisions
m (New page: <geshi lang="java5"> import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStr...) |
mNo edit summary |
||
Line 238: | Line 238: | ||
} | } | ||
</geshi> | </geshi> | ||
[[Category:Java]] |
Revision as of 01:14, 24 November 2008
<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.Arrays; 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
:
*
*
-
*
*
- 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 thehashCode
value to change if the information * used in theequals
method changes.
*
*
* - If two objects are equal according to the
equals
method, * they must return the same value fromhashCode
.
*
*
*
* - The
equals
method is reflexive, which means that an * object is equal to itself:x.equals(x)
should return true.
*
*
* - The
equals
method is symmetric: If *x.equals(y)
returns true, theny.equals(x)
* should return true also.
*
*
*
* - The
equals
method is transitive: If *x.equals(y)
returns true andy.equals(z)
* returns true, thenx.equals(z)
should return true.
*
*
* - The
equals
method is consistent.x.equals(y)
* should consistently return either true or false. The Java 2 javadoc clarifies * that the result ofx.equals(y)
can change if the information * used in the equals comparisons change.
*
*
*
* - Finally,
x.equals(null)
should return false.
*
* * 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) * * @author brianegge */
public abstract class EqualityTestCase 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 Object getA();
/** * * @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 Object getB();
/** * @return an object which is not equal to either A or B. This method can * optionally be implemented. */ protected Object getC() { return null; }
public void testEquals() { 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 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() { 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 set = new HashSet(); set.add(getA());
assertTrue(set.contains(getA())); assertFalse(set.contains(getB())); assertFalse(set.contains(getC())); }
public void testComparable() throws Exception { if (getA() instanceof Comparable) { assertEquals(0, ((Comparable) getA()).compareTo(getA())); assertEquals(0, ((Comparable) getB()).compareTo(getB()));
assertTrue(((Comparable) getA()).compareTo(getB()) < 0); assertTrue(((Comparable) getB()).compareTo(getA()) > 0); if (getC() != null) { assertEquals(0, ((Comparable) getC()).compareTo(getC())); assertTrue(((Comparable) getA()).compareTo(getC()) < 0); assertTrue(((Comparable) getB()).compareTo(getC()) < 0); assertTrue(((Comparable) getC()).compareTo(getB()) > 0); } } }
public void testSerializable() throws Exception { if (getA() instanceof Serializable) { for (int i = 0; i < objects().length; i++) { Object o = objects()[i]; assertEquals(o, 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 SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (getA() instanceof Cloneable) { for (int i = 0; i < objects().length; i++) { Object o = objects()[i]; 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() {
for (Iterator iterator = getObjects().iterator(); iterator.hasNext();) {
Object o = iterator.next();
assertNotNull(o.toString());
}
}
/** * This will either return an array of one or two objects depending on if getC() is implemented. * @return a list of test objects. */ private Object[] objects() { if (getC() != null) { return new Object[] { getA(), getB(), getC() }; } return new Object[] { getA(), getB() }; }
private List getObjects() { return Arrays.asList(objects()); } } </geshi>