views:

553

answers:

8

This question is close, but still not what I want. I'd like to assert in a generic way that two bean objects are equivalent. In case they are not, I'd like a detailed error message explaining the difference instead of a boolean "equal" or "not equal".

+1  A: 

I would suggest to override toString() method, and use that in your assertion. For example

assertEquals(obj1.toString, obj2.toString());
Adeel Ansari
I'd prefer something that doesn't force me to implement toString()
ripper234
Write a method toFormattedString(), which will give you the option of using default implementation of toString(). Anyways, I recommend to override toString(), if there is no compelling reason. You will also find this tip in Effective Java by Joshua Bloch.
Adeel Ansari
A: 

Since you didn't like the answers in the question you referenced, why not just have a toXml method in each bean, turn them into an xml file and then use xmlUnit to compare.

You can get more info on comparing xml files here:

http://stackoverflow.com/questions/141993/best-way-to-compare-2-xml-documents-in-java

James Black
You don't have to use toXml(). Just use [XMLEncoder][http://java.sun.com/j2se/1.5.0/docs/api/java/beans/XMLEncoder.html]
Aaron Digulla
My only concern is that some properties may need to be excluded, by using toXml() it forces that he knows what is being compared, otherwise your comment would be more correct.
James Black
+2  A: 

You can use Commons Lang's ToStringBuilder to convert both of them into readable strings and then use assertEquals() on both strings.

If you like XML, you can use java.lang.XMLEncoder to turn your bean into XML and then compare the two XML documents.

Personally, I prefer ToStringBuilder since it gives you more control over the formatting and allows you to do things like sorting the elements in a set to avoid false negatives.

I suggest to put each field of the bean in a different line to make it much more simple to compare them (see my blog for details).

Aaron Digulla
Great, I didn't know about this. Actually, I need something like this badly. Thanks pal.
Adeel Ansari
A: 

You're not really asserting equality, more doing a "diff". Clearly, the meaning of "same" depends upon particular logic for each type, and the representation of the difference also may vary. One major difference between this requirment and a conventional equals() is that usually equals() will stop as soon as the first difference is seen, you will want to carry on and compare every field.

I would look at reusing some of the equals() patterns, but I suspect you'll need to write your own code.

djna
A: 

The first quesion I'd have to ask if is, do you want to do 'deep' equals on the Bean? does it have child beans that need to be tested? You can override the equals method, but this only returns a boolean, so you could create a 'comparator' and that could throw an exception with a message about what was not equal.

In the following examples, I've listed a few ways to implement the equals method.

if you want to check if they are the same object instance, then the normal equals method from Object will tell you.

    objectA.equals(objectB);

if you want to write a customer equals method to check that all the member varibles of an object make them equal then you can override the equals method like this...

  /**
     * Method to check the following...
     * <br>
     * <ul>
     *    <li>getTitle</li>
     *    <li>getInitials</li>
     *    <li>getForename</li>
     *    <li>getSurname</li>
     *    <li>getSurnamePrefix</li>
     * </ul>
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj)
    {
      if (   (!compare(((ICustomer) obj).getTitle(), this.getTitle()))
          || (!compare(((ICustomer) obj).getInitials(), this.getInitials()))
          || (!compare(((ICustomer) obj).getForename(), this.getForename()))
          || (!compare(((ICustomer) obj).getSurname(), this.getSurname()))
          || (!compare(((ICustomer) obj).getSurnamePrefix(), this.getSurnamePrefix()))
          || (!compare(((ICustomer) obj).getSalutation(), this.getSalutation()))  ){
          return false;
      }
      return true;
    }

The last option is to use java reflection to check all the member varibles in the equals method. This is great if you really want to check every member varible via its bean get/set method. It wont (I dont think) allow you to check private memeber varibles when testing of the two objects are the same. (not if your object model has a circular dependancy, dont do this, it will never return)

NOTE: this is not my code, it comes from...

Java Reflection equals public static boolean equals(Object bean1, Object bean2) { // Handle the trivial cases if (bean1 == bean2) return true;

if (bean1 == null)
return false;

if (bean2 == null)
return false;

// Get the class of one of the parameters
Class clazz = bean1.getClass();

// Make sure bean1 and bean2 are the same class
if (!clazz.equals(bean2.getClass()))
{
return false;
}

// Iterate through each field looking for differences
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
// setAccessible is great (encapsulation
// purists will disagree), setting to true
// allows reflection to have access to
// private members.
fields[i].setAccessible(true);
try
{
Object value1 = fields[i].get(bean1);
Object value2 = fields[i].get(bean2);

if ((value1 == null && value2 != null) ||
(value1 != null && value2 == null))
{
return false;
}

if (value1 != null &&
value2 != null &&
!value1.equals(value2))
{
return false;
}
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}

return true;

The one thing that this does not do it to tell you the reason for the difference, but that could be done via message to Log4J when you find a section that is not equal.

jeff porter
-1 This just says "there is a difference" but gives you no clue what the difference might be.
Aaron Digulla
so in the real code, you can put a message out to log4j.
jeff porter
It shouldn't be necessary to even have logs while unit testing. The failed tests should tell you all you need to know.
Michael Rutherfurd
+1  A: 

I think, the most generic approach is to reflect the bean members and test them for equality one-by-one. The common lang's EqualsBuilder is a good start and it should be not a big deal, to adapt it (on source level) to your requirements (reporting the differences instead of returning the equals result).

Andreas_D
-1 That doesn't give a detailed error message what is different.
Aaron Digulla
I used this as well but with a slight different. In order to also get a meaningful error message after using EqualsBuilder. reflectionEquals I do an assert on the two ToStringBuilder.reflectionToString (finally with a fail(...) should the to string be identical actually successed). Eclipse will then highlight the diffrence between the two objects.
mlk
@Aaron - I know, and that's why I suggested to adapt the EqualsBuilder code.
Andreas_D
A: 

I am assuming here that both beans are of the same type, in which case only the member variable values will differ across bean instances.

Define an util class (public static final with private ctor) called, say, BeanAssertEquals. Use Java reflection to obtain the value of each member variable in each bean. Then do an equals() between values for the same member variable in different beans. If an equality fails, mention the field name.

Note: member variables are usually private, so you would need to use reflection to temporarily change the accessibility of private members.

Additionally, depending how fine-grained you want the assertion to work, you should consider the following:

  1. Equality of member variables not in the bean class but all superclasses.

  2. Equality of elements in arrays, in case a member variable is of type array.

  3. For two values of a given member across beans, you might consider doing BeanAssertEquals.assertEquals(value1, value2) instead of value1.equals(value2).

Indroneel Das
A: 

(to build on my comment to Andreas_D above)

/** Asserts two objects are equals using a reflective equals.
 * 
 * @param message The message to display.
 * @param expected The expected result.
 * @param actual The actual result
 */
public static void assertReflectiveEquals(final String message, 
  final Object expected, final Object actual) {
 if (!EqualsBuilder.reflectionEquals(expected, actual)) {
  assertEquals(message, 
    reflectionToString(expected, ToStringStyle.SHORT_PREFIX_STYLE), 
    reflectionToString(actual, ToStringStyle.SHORT_PREFIX_STYLE));
  fail(message + "expected: <" + expected + ">  actual: <" + actual + ">");
 }
}

This is what I use, and I believe it meets all basic requirements. By doing the assert on the reflective ToString then Eclipse will highlight the difference.

While Hamcrest can offer a much nicer message, this does involve a good deal less code.

mlk