View Javadoc

1   package com.diasparsoftware.util.junit;
2   
3   import java.util.*;
4   
5   import junit.framework.*;
6   
7   import org.apache.commons.collections.*;
8   
9   import com.diasparsoftware.java.util.*;
10  
11  public abstract class ValueObjectEqualsTest extends TestCase {
12      private Object control;
13      private Map differentObjects = new HashMap();
14  
15      private Object equalToControl;
16      private Object equalToControl2;
17  
18      private static final int NUM_ITERATIONS = 20;
19  
20      /***
21       * Creates the "control" instance of the class under test —
22       * the object against which all the others are to be compared.
23      */
24      protected abstract Object createControlInstance() throws Exception;
25  
26      /***
27       * Creates and returns an instance of the class under test that
28       * differs from the control instance by having a different value
29       * for the specified key property.
30       */
31      protected abstract Object createInstanceDiffersIn(String keyPropertyName)
32          throws Exception;
33  
34      /***
35       * The names of the key properties used to distinguish
36       * unequal instances of this class.
37       * 
38       * @return
39       */
40      protected abstract List keyPropertyNames();
41  
42      protected void setUp() throws Exception {
43          super.setUp();
44  
45          control = createControlInstance();
46          equalToControl = createControlInstance();
47          equalToControl2 = createControlInstance();
48  
49          CollectionUtil
50              .forEachDo(keyPropertyNames(), new ExceptionalClosure() {
51              public Object execute(Object each) throws Exception {
52                  String eachName = (String) each;
53                  differentObjects.put(
54                      each,
55                      createInstanceDiffersIn(eachName));
56                  return null;
57              }
58          });
59  
60          // We want these assertions to yield errors, not failures.
61          try {
62              assertNotNull(
63                  "createControlInstance() returned null",
64                  control);
65              assertNotNull(
66                  "2nd createControlInstance() returned null",
67                  equalToControl);
68              assertNotNull(
69                  "3rd createControlInstance() returned null",
70                  equalToControl2);
71  
72              eachDifferentObjectDo(new MapEntryClosure() {
73                  public void eachMapEntry(Object key, Object value) {
74                      assertNotNull(nameOf(key) + "returned null", value);
75                  }
76              });
77  
78              Assert.assertNotSame(control, equalToControl);
79              Assert.assertNotSame(control, equalToControl2);
80  
81              eachDifferentObjectDo(new MapEntryClosure() {
82                  public void eachMapEntry(Object key, Object value) {
83                      Assert.assertNotSame(
84                          nameOf(key) + " same as control",
85                          control,
86                          value);
87                      Assert.assertNotSame(
88                          nameOf(key) + " same as equalToControl",
89                          equalToControl,
90                          value);
91                      Assert.assertNotSame(
92                          nameOf(key) + " same as equalToControl2",
93                          equalToControl2,
94                          value);
95                  }
96              });
97  
98              Assert.assertNotSame(equalToControl, equalToControl2);
99  
100             assertEquals(
101                 "1st and 2nd equal instances of different classes",
102                 control.getClass(),
103                 equalToControl.getClass());
104             assertEquals(
105                 "1st and 3rd equal instances of different classes",
106                 control.getClass(),
107                 equalToControl2.getClass());
108 
109             eachDifferentObjectDo(new MapEntryClosure() {
110                 public void eachMapEntry(Object key, Object value) {
111                     assertEquals(
112                         "control instance and "
113                             + nameOf(key)
114                             + " of different classes",
115                         control.getClass(),
116                         value.getClass());
117 
118                 }
119             });
120         }
121         catch (AssertionFailedError ex) {
122             throw new IllegalArgumentException(ex.getMessage());
123         }
124     }
125 
126     /***
127      * Tests whether <code>equals</code> holds up against a new
128      * <code>Object</code> (should always be <code>false</code>).
129      */
130     public final void testEqualsAgainstNewObject() {
131         final Object o = new Object();
132 
133         assertNotEquals(o, control);
134         assertNotEquals(o, equalToControl);
135         assertNotEquals(o, equalToControl2);
136 
137         eachDifferentObjectDo(new MapEntryClosure() {
138             public void eachMapEntry(Object key, Object value) {
139                 assertNotEquals(o, value);
140             }
141         });
142     }
143 
144     /***
145      * Tests whether <code>equals</code> holds up against <code>null</code>.
146      */
147     public final void testEqualsAgainstNull() {
148         assertNotEquals("null vs. 1st", null, control);
149         assertNotEquals("null vs. 2nd", null, equalToControl);
150         assertNotEquals("null vs. 3rd", null, equalToControl2);
151 
152         eachDifferentObjectDo(new MapEntryClosure() {
153             public void eachMapEntry(Object key, Object value) {
154                 assertNotEquals("null vs. " + nameOf(key), null, value);
155             }
156         });
157     }
158 
159     /***
160      * Tests whether <code>equals</code> holds up against objects that should
161      * not compare equal.
162      */
163     public final void testEqualsAgainstUnequalObjects() {
164         eachDifferentObjectDo(new MapEntryClosure() {
165             public void eachMapEntry(Object key, Object value) {
166                 assertNotEquals(
167                     "1st vs. " + nameOf(key),
168                     control,
169                     value);
170                 assertNotEquals(
171                     "2nd vs. " + nameOf(key),
172                     equalToControl,
173                     value);
174                 assertNotEquals(
175                     "3rd vs. " + nameOf(key),
176                     equalToControl2,
177                     value);
178 
179                 assertNotEquals(
180                     nameOf(key) + " vs. 1st",
181                     value,
182                     control);
183                 assertNotEquals(
184                     nameOf(key) + " vs. 2nd",
185                     value,
186                     equalToControl);
187                 assertNotEquals(
188                     nameOf(key) + " vs. 3rd",
189                     value,
190                     equalToControl2);
191             }
192         });
193 
194     }
195 
196     /***
197      * Tests whether <code>equals</code> is <em>consistent</em>.
198      */
199     public final void testEqualsIsConsistentAcrossInvocations() {
200         for (int i = 0; i < NUM_ITERATIONS; ++i) {
201             testEqualsAgainstNewObject();
202             testEqualsAgainstNull();
203             testEqualsAgainstUnequalObjects();
204             testEqualsIsReflexive();
205             testEqualsIsSymmetricAndTransitive();
206         }
207     }
208 
209     /***
210      * Tests whether <code>equals</code> is <em>reflexive</em>.
211      */
212     public final void testEqualsIsReflexive() {
213         assertEquals("1st equal instance", control, control);
214         assertEquals(
215             "2nd equal instance",
216             equalToControl,
217             equalToControl);
218         assertEquals(
219             "3rd equal instance",
220             equalToControl2,
221             equalToControl2);
222 
223         eachDifferentObjectDo(new MapEntryClosure() {
224             public void eachMapEntry(Object key, Object value) {
225                 assertEquals(nameOf(key) + " instance", value, value);
226             }
227         });
228     }
229 
230     /***
231      * Tests whether <code>equals</code> is <em>symmetric</em> and
232      * <em>transitive</em>.
233      */
234     public final void testEqualsIsSymmetricAndTransitive() {
235         assertEquals("1st vs. 2nd", control, equalToControl);
236         assertEquals("2nd vs. 1st", equalToControl, control);
237 
238         assertEquals("1st vs. 3rd", control, equalToControl2);
239         assertEquals("3rd vs. 1st", equalToControl2, control);
240 
241         assertEquals("2nd vs. 3rd", equalToControl, equalToControl2);
242         assertEquals("3rd vs. 2nd", equalToControl2, equalToControl);
243     }
244 
245     /***
246      * Tests the <code>hashCode</code> contract.
247      */
248     public final void testHashCodeContract() {
249         assertEquals(
250             "1st vs. 2nd",
251             control.hashCode(),
252             equalToControl.hashCode());
253         assertEquals(
254             "1st vs. 3rd",
255             control.hashCode(),
256             equalToControl2.hashCode());
257         assertEquals(
258             "2nd vs. 3rd",
259             equalToControl.hashCode(),
260             equalToControl2.hashCode());
261     }
262 
263     /***
264      * Tests the consistency of <code>hashCode</code>.
265      */
266     public final void testHashCodeIsConsistentAcrossInvocations() {
267         int eq1Hash = control.hashCode();
268         int eq2Hash = equalToControl.hashCode();
269         int eq3Hash = equalToControl2.hashCode();
270 
271         final Map differentObjectsHashes = new HashMap();
272 
273         eachDifferentObjectDo(new MapEntryClosure() {
274             public void eachMapEntry(Object key, Object value) {
275                 differentObjectsHashes.put(
276                     key,
277                     new Integer(value.hashCode()));
278             }
279         });
280 
281         for (int i = 0; i < NUM_ITERATIONS; ++i) {
282             assertEquals(
283                 "1st equal instance",
284                 eq1Hash,
285                 control.hashCode());
286             assertEquals(
287                 "2nd equal instance",
288                 eq2Hash,
289                 equalToControl.hashCode());
290             assertEquals(
291                 "3rd equal instance",
292                 eq3Hash,
293                 equalToControl2.hashCode());
294 
295             eachDifferentObjectDo(new MapEntryClosure() {
296                 public void eachMapEntry(Object key, Object value) {
297                     assertEquals(
298                         nameOf(key) + " instance",
299                         ((Integer) differentObjectsHashes.get(key))
300                             .intValue(),
301                         value.hashCode());
302                 }
303             });
304         }
305     }
306 
307     protected static void assertNotEquals(Object lhs, Object rhs) {
308         assertNotEquals(null, lhs, rhs);
309     }
310 
311     protected static void assertNotEquals(
312         String failureMessage,
313         Object lhs,
314         Object rhs) {
315         if (lhs != null)
316             assertFalse(failureMessage, lhs.equals(rhs));
317     }
318 
319     private void eachDifferentObjectDo(Closure closure) {
320         CollectionUtils.forAllDo(differentObjects.entrySet(), closure);
321     }
322 
323     private final String nameOf(Object key) {
324         return "objectDiffersBy('" + key + "')";
325     }
326 }