Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep reflective compare equals

I am trying to validate serialize and de-serialize routines by comparing the resulting object with the original object. The routines can serialize arbitrary and deeply nested classes and consequently I want a comparison routine which can be given the original and final instance and reflectively walk through each value type and compare the values and iteratively dive into reference types to compare values.

I have tried the Apache Commons Lang EqualsBuilder.reflectionEquals(inst1, inst2) but this does not appear to do a very deep comparison, it simply compares reference types for equality rather than diving deeper into them:

The following code illustrates my issue. The first call to reflectionEquals returns true but the second returns false.

Is there a library routine anyone could recommend?

class dummy {
    dummy2 nestedClass;
}

class dummy2 {
    int intVal;
}

@Test
public void testRefEqu() {

    dummy inst1 = new dummy();
    inst1.nestedClass = new dummy2();
    inst1.nestedClass.intVal = 2;
    dummy inst2 = new dummy();
    inst2.nestedClass = new dummy2();
    inst2.nestedClass.intVal = 2;
    boolean isEqual = EqualsBuilder.reflectionEquals(inst1.nestedClass, inst2.nestedClass);
    isEqual = EqualsBuilder.reflectionEquals(inst1, inst2);
}
like image 539
Howard May Avatar asked Mar 09 '12 10:03

Howard May


3 Answers

From the answer to this question https://stackoverflow.com/a/1449051/116509 and from some preliminary testing, it looks like Unitils' ReflectionAssert.assertReflectionEquals does what you're expecting. (Edit: but may be abandoned, so you could try AssertJ https://assertj.github.io/doc/#assertj-core-recursive-comparison)

2021 edit: EqualsBuilder now has a testRecursive option. However the unit test libraries mentioned will give you a better failure message to help debug, so depending on context, they're still the best option.

like image 119
artbristol Avatar answered Oct 27 '22 00:10

artbristol


One method would be to compare objects using reflection - but this is tricky. Another strategy would be to compare byte arrays of serialized objects:

class dummy implements Serializable {
    dummy2 nestedClass;
}

class dummy2  implements Serializable {
    int intVal;
}

@Test
public void testRefEqu() throws IOException {

    dummy inst1 = new dummy();
    inst1.nestedClass = new dummy2();
    inst1.nestedClass.intVal = 2;

    dummy inst2 = new dummy();
    inst2.nestedClass = new dummy2();
    inst2.nestedClass.intVal = 2;

    boolean isEqual1 = EqualsBuilder.reflectionEquals(inst1.nestedClass, inst2.nestedClass);
    boolean isEqual2 = EqualsBuilder.reflectionEquals(inst1, inst2);

    System.out.println(isEqual1);
    System.out. println(isEqual2);

    ByteArrayOutputStream baos1 =new ByteArrayOutputStream();
    ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
    oos1.writeObject(inst1);
    oos1.close();

    ByteArrayOutputStream baos2 =new ByteArrayOutputStream();
    ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
    oos2.writeObject(inst1);
    oos2.close();

    byte[] arr1 = baos1.toByteArray();
    byte[] arr2 = baos2.toByteArray();

    boolean isEqual3 = Arrays.equals(arr1, arr2);

    System.out.println(isEqual3);

}

Your application serializes and deserializes objects so this approach seems to be the fastest solution (in terms of CPU operations) for your problem.

like image 41
whysoserious Avatar answered Oct 27 '22 02:10

whysoserious


You can use AssertJ's field by field recursive comparison feature, for example:

import static org.assertj.core.api.BDDAssertions.then;

then(actualObject).usingRecursiveComparison().isEqualTo(expectedObject);
like image 3
yurez Avatar answered Oct 27 '22 02:10

yurez