001: /*
002: * Copyright 2006-2007, Unitils.org
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.unitils.reflectionassert;
017:
018: import junit.framework.Assert;
019: import static junit.framework.Assert.assertNotNull;
020: import junit.framework.AssertionFailedError;
021: import ognl.DefaultMemberAccess;
022: import ognl.Ognl;
023: import ognl.OgnlContext;
024: import ognl.OgnlException;
025: import org.apache.commons.collections.CollectionUtils;
026: import org.apache.commons.collections.Transformer;
027: import org.apache.commons.lang.StringUtils;
028: import org.unitils.core.UnitilsException;
029: import org.unitils.reflectionassert.ReflectionComparator.Difference;
030: import static org.unitils.reflectionassert.ReflectionComparatorMode.IGNORE_DEFAULTS;
031: import static org.unitils.reflectionassert.ReflectionComparatorMode.LENIENT_ORDER;
032:
033: import java.util.Arrays;
034: import java.util.Collection;
035:
036: /**
037: * A class for asserting that 2 objects/collections are equal by comparing properties and fields of the
038: * objects/collections using reflection.
039: * <p/>
040: * The (combination of) comparator modes specify how strict the comparison must be:<ul>
041: * <li>ignore defaults: compare only arguments (and inner values) that have a non default value (eg null) as exepected value</li>
042: * <li>lenient dates: do not compare actual date values, just that they both have a value or not</li>
043: * <li>lenient order: order is not important when comparing collections or arrays</li>
044: * </ul>
045: * <p/>
046: * There are 2 versions of each method: a len and a ref verion.
047: * With the ref versions you can set the comparator modes explicitly (note: no modes means strict comparisson). The len
048: * versions are the same as the ref versions but have lenient order and ignore defaults set by default.
049: * <p/>
050: * The name assert..RefEquals is chosen instead of assert..Equals so it can be added as a static import
051: * without naming collisions.
052: *
053: * @author Tim Ducheyne
054: * @author Filip Neven
055: * @see ReflectionComparator
056: * @see ReflectionComparatorMode
057: */
058: public class ReflectionAssert {
059:
060: /**
061: * Asserts that two objects are equal. Reflection is used to compare all fields of these values.
062: * If they are not equal an AssertionFailedError is thrown.
063: * <p/>
064: * This is identical to {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} with
065: * lenient order and ignore defaults set as comparator modes.
066: *
067: * @param expected the expected object
068: * @param actual the given object
069: * @throws AssertionFailedError when both objects are not equals
070: */
071: public static void assertLenEquals(Object expected, Object actual)
072: throws AssertionFailedError {
073: assertLenEquals(null, expected, actual);
074: }
075:
076: /**
077: * Asserts that two objects are equal. Reflection is used to compare all fields of these values.
078: * If they are not equal an AssertionFailedError is thrown.
079: * <p/>
080: * The comparator modes determine how strict to compare the values.
081: *
082: * @param expected the expected object
083: * @param actual the given object
084: * @param modes the comparator modes
085: * @throws AssertionFailedError when both objects are not equals
086: */
087: public static void assertRefEquals(Object expected, Object actual,
088: ReflectionComparatorMode... modes)
089: throws AssertionFailedError {
090: assertRefEquals(null, expected, actual, modes);
091: }
092:
093: /**
094: * Asserts that two objects are equal. Reflection is used to compare all fields of these values.
095: * If they are not equal an AssertionFailedError is thrown.
096: * <p/>
097: * This is identical to {@link #assertRefEquals(String,Object,Object,ReflectionComparatorMode...)} with
098: * lenient order and ignore defaults set as comparator modes.
099: *
100: * @param message a message for when the assertion fails
101: * @param expected the expected object
102: * @param actual the given object
103: * @throws AssertionFailedError when both objects are not equals
104: */
105: public static void assertLenEquals(String message, Object expected,
106: Object actual) throws AssertionFailedError {
107: assertRefEquals(message, expected, actual, LENIENT_ORDER,
108: IGNORE_DEFAULTS);
109: }
110:
111: /**
112: * Asserts that two objects are equal. Reflection is used to compare all fields of these values.
113: * If they are not equal an AssertionFailedError is thrown.
114: * <p/>
115: * The comparator modes determine how strict to compare the values.
116: *
117: * @param message a message for when the assertion fails
118: * @param expected the expected object
119: * @param actual the given object
120: * @param modes the comparator modes
121: * @throws AssertionFailedError when both objects are not equals
122: */
123: public static void assertRefEquals(String message, Object expected,
124: Object actual, ReflectionComparatorMode... modes)
125: throws AssertionFailedError {
126: ReflectionComparator reflectionComparator = ReflectionComparatorChainFactory
127: .getComparatorChainForModes(modes);
128: Difference difference = reflectionComparator.getDifference(
129: expected, actual);
130: if (difference != null) {
131: Assert.fail(formatMessage(message, difference));
132: }
133: }
134:
135: /**
136: * Asserts that the value of a property of an object is equal to the given value.
137: * <p/>
138: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
139: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
140: * <p/>
141: * This is identical to {@link #assertPropertyRefEquals(String,Object,Object,ReflectionComparatorMode...)} with
142: * lenient order and ignore defaults set as comparator modes.
143: *
144: * @param propertyName the property, not null
145: * @param expectedPropertyValue the expected value
146: * @param actualObject the object that contains the property
147: * @throws AssertionFailedError when both objects are not equals
148: */
149: public static void assertPropertyLenEquals(String propertyName,
150: Object expectedPropertyValue, Object actualObject)
151: throws AssertionFailedError {
152: assertPropertyLenEquals(null, propertyName,
153: expectedPropertyValue, actualObject);
154: }
155:
156: /**
157: * Asserts that the value of a property of an object is equal to the given value.
158: * <p/>
159: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
160: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
161: * <p/>
162: * The comparator modes determine how strict to compare the values.
163: *
164: * @param propertyName the property, not null
165: * @param expectedPropertyValue the expected value
166: * @param actualObject the object that contains the property
167: * @param modes the comparator modes
168: * @throws AssertionFailedError when both objects are not equals
169: */
170: public static void assertPropertyRefEquals(String propertyName,
171: Object expectedPropertyValue, Object actualObject,
172: ReflectionComparatorMode... modes)
173: throws AssertionFailedError {
174: assertPropertyRefEquals(null, propertyName,
175: expectedPropertyValue, actualObject, modes);
176: }
177:
178: /**
179: * Asserts that the value of a property of an object is equal to the given value.
180: * <p/>
181: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
182: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
183: * <p/>
184: * This is identical to {@link #assertPropertyRefEquals(String,String,Object,Object,ReflectionComparatorMode...)} with
185: * lenient order and ignore defaults set as comparator modes.
186: *
187: * @param message a message for when the assertion fails
188: * @param propertyName the property, not null
189: * @param expectedPropertyValue the expected value
190: * @param actualObject the object that contains the property
191: * @throws AssertionFailedError when both objects are not equals
192: */
193: public static void assertPropertyLenEquals(String message,
194: String propertyName, Object expectedPropertyValue,
195: Object actualObject) throws AssertionFailedError {
196: assertPropertyRefEquals(message, propertyName,
197: expectedPropertyValue, actualObject, LENIENT_ORDER,
198: IGNORE_DEFAULTS);
199: }
200:
201: /**
202: * Asserts that the value of a property of an object is equal to the given value.
203: * <p/>
204: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
205: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
206: * <p/>
207: * The comparator modes determine how strict to compare the values.
208: *
209: * @param message a message for when the assertion fails
210: * @param propertyName the property, not null
211: * @param expectedPropertyValue the expected value
212: * @param actualObject the object that contains the property
213: * @param modes the comparator modes
214: * @throws AssertionFailedError when both objects are not equals
215: */
216: public static void assertPropertyRefEquals(String message,
217: String propertyName, Object expectedPropertyValue,
218: Object actualObject, ReflectionComparatorMode... modes)
219: throws AssertionFailedError {
220: assertNotNull("Actual object is null.", actualObject);
221: Object propertyValue = getProperty(actualObject, propertyName);
222: String formattedMessage = formatMessage(message,
223: "Incorrect value for property: " + propertyName);
224: assertRefEquals(formattedMessage, expectedPropertyValue,
225: propertyValue, modes);
226: }
227:
228: /**
229: * Asserts that a property of all objects in the collection are equal to the given values.
230: * <p/>
231: * Example: assertPropertyEquals("id", myIdCollection, myObjectCollection) checks whether all values of the
232: * id field of the myObjectCollection elements matches the values in the myIdCollection
233: * <p/>
234: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
235: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
236: * <p/>
237: * This is identical to {@link #assertPropertyRefEquals(String,Collection,Collection,ReflectionComparatorMode...)} with
238: * lenient order and ignore defaults set as comparator modes.
239: *
240: * @param propertyName the property, not null
241: * @param expectedPropertyValues the expected values
242: * @param actualObjects the objects that contain the property
243: * @throws AssertionFailedError when both objects are not equals
244: */
245: public static void assertPropertyLenEquals(String propertyName,
246: Collection<?> expectedPropertyValues,
247: Collection<?> actualObjects) throws AssertionFailedError {
248: assertPropertyLenEquals(null, propertyName,
249: expectedPropertyValues, actualObjects);
250: }
251:
252: /**
253: * Asserts that a property of all objects in the collection are equal to the given values.
254: * <p/>
255: * Example: assertPropertyEquals("id", myIdCollection, myObjectCollection) checks whether all values of the
256: * id field of the myObjectCollection elements matches the values in the myIdCollection
257: * <p/>
258: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
259: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
260: * <p/>
261: * The comparator modes determine how strict to compare the values.
262: *
263: * @param propertyName the property, not null
264: * @param expectedPropertyValues the expected values
265: * @param actualObjects the objects that contain the property
266: * @param modes the comparator modes
267: * @throws AssertionFailedError when both objects are not equals
268: */
269: public static void assertPropertyRefEquals(String propertyName,
270: Collection<?> expectedPropertyValues,
271: Collection<?> actualObjects,
272: ReflectionComparatorMode... modes)
273: throws AssertionFailedError {
274: assertPropertyRefEquals(null, propertyName,
275: expectedPropertyValues, actualObjects, modes);
276: }
277:
278: /**
279: * Asserts that a property of all objects in the collection are equal to the given values.
280: * <p/>
281: * Example: assertPropertyEquals("id", myIdCollection, myObjectCollection) checks whether all values of the
282: * id field of the myObjectCollection elements matches the values in the myIdCollection
283: * <p/>
284: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
285: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
286: * <p/>
287: * This is identical to {@link #assertPropertyRefEquals(String,String,Collection,Collection,ReflectionComparatorMode...)} with
288: * lenient order and ignore defaults set as comparator modes.
289: *
290: * @param message a message for when the assertion fails
291: * @param propertyName the property, not null
292: * @param expectedPropertyValues the expected values, not null
293: * @param actualObjects the objects that contain the property
294: * @throws AssertionFailedError when both objects are not equals
295: */
296: public static void assertPropertyLenEquals(String message,
297: String propertyName, Collection<?> expectedPropertyValues,
298: Collection<?> actualObjects) throws AssertionFailedError {
299: assertPropertyRefEquals(message, propertyName,
300: expectedPropertyValues, actualObjects, LENIENT_ORDER,
301: IGNORE_DEFAULTS);
302: }
303:
304: /**
305: * Asserts that a property of all objects in the collection are equal to the given values.
306: * <p/>
307: * Example: assertPropertyEquals("id", myIdCollection, myObjectCollection) checks whether all values of the
308: * id field of the myObjectCollection elements matches the values in the myIdCollection
309: * <p/>
310: * Bean notation can be used to specify inner properties. Eg myArray[2].innerValue.
311: * {@link #assertRefEquals(Object,Object,ReflectionComparatorMode...)} is used to check whether both values are equal.
312: * <p/>
313: * The comparator modes determine how strict to compare the values.
314: *
315: * @param message a message for when the assertion fails
316: * @param propertyName the property, not null
317: * @param expectedPropertyValues the expected values, not null
318: * @param actualObjects the objects that contain the property
319: * @param modes the comparator modes
320: * @throws AssertionFailedError when both objects are not equals
321: */
322: public static void assertPropertyRefEquals(String message,
323: String propertyName, Collection<?> expectedPropertyValues,
324: Collection<?> actualObjects,
325: ReflectionComparatorMode... modes)
326: throws AssertionFailedError {
327: assertNotNull("Actual object list is null.", actualObjects);
328: Collection<?> actualPropertyValues = CollectionUtils.collect(
329: actualObjects, new OgnlTransformer(propertyName));
330: assertRefEquals(message, expectedPropertyValues,
331: actualPropertyValues, modes);
332: }
333:
334: /**
335: * Formats the exception message.
336: *
337: * @param suppliedMessage the user supplied message
338: * @param difference the difference
339: * @return the formatted message
340: */
341: protected static String formatMessage(String suppliedMessage,
342: Difference difference) {
343: String result = formatMessage(suppliedMessage, difference
344: .getMessage());
345:
346: String fieldString = difference.getFieldStackAsString();
347: if (StringUtils.isEmpty(fieldString)) {
348: fieldString = "<top-level>";
349: }
350:
351: result += "\nField: <" + fieldString;
352: result += "> expected: <"
353: + formatObject(difference.getLeftValue());
354: result += "> but was: <"
355: + formatObject(difference.getRightValue()) + ">";
356: return result;
357: }
358:
359: /**
360: * Formats the exception message.
361: *
362: * @param suppliedMessage the user supplied message
363: * @param specificMessage the reason
364: * @return the formatted message
365: */
366: protected static String formatMessage(String suppliedMessage,
367: String specificMessage) {
368: if (StringUtils.isEmpty(suppliedMessage)) {
369: return specificMessage;
370: }
371: return suppliedMessage + "\n" + specificMessage;
372: }
373:
374: /**
375: * Gets the string representation of the given object. This also correctly handles array types.
376: *
377: * @param object The instance
378: * @return The string representation, not null
379: */
380: protected static String formatObject(Object object) {
381: if (object instanceof byte[]) {
382: return Arrays.toString((byte[]) object);
383:
384: } else if (object instanceof short[]) {
385: return Arrays.toString((short[]) object);
386:
387: } else if (object instanceof int[]) {
388: return Arrays.toString((int[]) object);
389:
390: } else if (object instanceof long[]) {
391: return Arrays.toString((long[]) object);
392:
393: } else if (object instanceof char[]) {
394: return Arrays.toString((char[]) object);
395:
396: } else if (object instanceof float[]) {
397: return Arrays.toString((float[]) object);
398:
399: } else if (object instanceof double[]) {
400: return Arrays.toString((double[]) object);
401:
402: } else if (object instanceof boolean[]) {
403: return Arrays.toString((boolean[]) object);
404:
405: } else if (object instanceof Object[]) {
406: return Arrays.toString((Object[]) object);
407: }
408: return String.valueOf(object);
409: }
410:
411: /**
412: * Evaluates the given OGNL expression, and returns the corresponding property value from the given object.
413: *
414: * @param object The object on which the expression is evaluated
415: * @param ognlExpression The OGNL expression that is evaluated
416: * @return The value for the given OGNL expression
417: */
418: protected static Object getProperty(Object object,
419: String ognlExpression) {
420: try {
421: OgnlContext ognlContext = new OgnlContext();
422: ognlContext.setMemberAccess(new DefaultMemberAccess(true));
423: Object ognlExprObj = Ognl.parseExpression(ognlExpression);
424: return Ognl.getValue(ognlExprObj, ognlContext, object);
425: } catch (OgnlException e) {
426: throw new UnitilsException(
427: "Failed to get property value using OGNL expression "
428: + ognlExpression, e);
429: }
430: }
431:
432: /**
433: * A commons collections transformer that takes an object and returns the value of the property that is
434: * specified by the given ognl expression.
435: */
436: protected static class OgnlTransformer implements Transformer {
437:
438: /* The ognl expression */
439: private String ognlExpression;
440:
441: /**
442: * Creates a transformer with the given ognl expression.
443: *
444: * @param ognlExpression The expression, not null
445: */
446: public OgnlTransformer(String ognlExpression) {
447: this .ognlExpression = ognlExpression;
448: }
449:
450: /**
451: * Transforms the given object in the value of the property that is specified by the ognl expression.
452: *
453: * @param object The object
454: * @return The value, null if object was null
455: */
456: public Object transform(Object object) {
457: if (object == null) {
458: return null;
459: }
460: return getProperty(object, ognlExpression);
461: }
462: }
463:
464: }
|