Search code examples
javaunit-testingassertj

Strange behavior of assertThat(actual).usingRecursiveComparison().ignoringAllOverriddenEquals().isEqualTo(expected)


I've come across an interesting behavior of recursive comparison in assertj library. If you compare objects of classes that are sub-classes, fields of super classes seem to be skipped during the comparison. Is that a known issue? Or am I doing something wrong? Here is a brief example:

import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class ExampleTest {

  @Test
  public void test() {
    MyException expected =
            new MyException("Expected text message");
    MyException actual = new MyException("Actual text message"); //values for the field Throwable.detailMessage are different
    assertThat(actual).usingRecursiveComparison().ignoringAllOverriddenEquals().isEqualTo(expected);
  }
}

class MyException extends RuntimeException {

  MyException(String message) {
    super(message);
  }
}

This test will pass when actually it should not as actual.getMessage() and expected.getMessage() will show different values.


Solution

  • Apparently, the lib skips comparison of the fields that are inherited from superclasses that reside in java.lang. As MyException is using a detailMessage field inherited from java.lang.Throwable, it is skipped. Here is the code from org.assertj.core.internal.Objects that seems to be responsible for such a behavior:

    /**
       * Returns the declared fields of given class and its superclasses stopping at superclass in <code>java.lang</code>
       * package whose fields are not included.
       *
       * @param clazz the class we want the declared fields.
       * @return the declared fields of given class and its superclasses.
       */
      public static Set<Field> getDeclaredFieldsIncludingInherited(Class<?> clazz) {
        checkNotNull(clazz, "expecting Class parameter not to be null");
        Set<Field> declaredFields = getDeclaredFieldsIgnoringSyntheticAndStatic(clazz);
        // get fields declared in superclass
        Class<?> superclazz = clazz.getSuperclass();
        while (superclazz != null && !superclazz.getName().startsWith("java.lang")) {
          declaredFields.addAll(getDeclaredFieldsIgnoringSyntheticAndStatic(superclazz));
          superclazz = superclazz.getSuperclass();
        }
        return declaredFields;
      }