Repository: assertj/assertj

Problem statement:
`isEqualToComparingFieldByFieldRecursively` throws NullPointerException while comparing null values
#### Summary

If a nested field is null while comparing equality for two objects with `isEqualToComparingFieldByFieldRecursively` a NullPointerException is thrown.

### Stacktrace

```
Exception in thread "main" java.lang.NullPointerException
	at org.assertj.core.error.ShouldBeEqualByComparingFieldByFieldRecursively.describeDifference(ShouldBeEqualByComparingFieldByFieldRecursively.java:67)
	at org.assertj.core.error.ShouldBeEqualByComparingFieldByFieldRecursively.shouldBeEqualByComparingFieldByFieldRecursive(ShouldBeEqualByComparingFieldByFieldRecursively.java:32)
	at org.assertj.core.internal.Objects.assertIsEqualToComparingFieldByFieldRecursively(Objects.java:728)
	at org.assertj.core.api.AbstractObjectAssert.isEqualToComparingFieldByFieldRecursively(AbstractObjectAssert.java:653)
...
```

#### Example

```java
class Main {

    static class Value {
        private final String v;

        public Value(String v) {
            this.v = v;
        }
    }

    public static void main(String[] args) {
        Value actual = new Value(null); //swapping actual and expected leads to the same error
        Value expected = new Value("something");
        Assertions.assertThat(expected).isEqualToComparingFieldByFieldRecursively(actual);
    }

}
```

#### Java 8 specific ?

* YES
Tested with Version 3.9.0



  


Hints:
Thanks for reporting this !
This is regression in version `2.9.0/3.9.0` introduced by https://github.com/joel-costigliola/assertj-core/commit/fb9f9522f7abe2d863af6aa760ec7e8162009f49

@hwielenberg As a workaround you can use `3.8.0`
  
I could write the Bugfix if you want.
Should be straightforward.
Contributions welcome! 😃 
I have a fix for this, will create pull request shortly.
backport to 2.x (fixed in 3.9.1)

Patch:
diff --git a/src/main/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively.java b/src/main/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively.java
--- a/src/main/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively.java
+++ b/src/main/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively.java
@@ -64,8 +64,8 @@ private static String describeDifference(Difference difference, Representation r
                   "- expected: <%s>%n" +
                   "- actual  : <%s>",
                   join(difference.getPath()).with("."),
-                  otherFieldValueRepresentation.replace("%", "%%"),
-                  actualFieldValueRepresentation.replace("%", "%%"));
+                  otherFieldValueRepresentation == null ? null : otherFieldValueRepresentation.replace("%", "%%"),
+                  actualFieldValueRepresentation == null ? null : actualFieldValueRepresentation.replace("%", "%%"));
   }
 
 }


Test patch:
diff --git a/src/test/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively_create_Test.java b/src/test/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively_create_Test.java
--- a/src/test/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively_create_Test.java
+++ b/src/test/java/org/assertj/core/error/ShouldBeEqualByComparingFieldByFieldRecursively_create_Test.java
@@ -24,11 +24,13 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 
+import org.assertj.core.api.ThrowableAssert;
 import org.assertj.core.description.TextDescription;
 import org.assertj.core.internal.DeepDifference;
 import org.assertj.core.internal.DeepDifference.Difference;
 import org.assertj.core.internal.objects.Objects_assertIsEqualToComparingFieldByFieldRecursive_Test.WithCollection;
 import org.assertj.core.internal.objects.Objects_assertIsEqualToComparingFieldByFieldRecursive_Test.WithMap;
+import org.assertj.core.test.Name;
 import org.junit.Test;
 
 public class ShouldBeEqualByComparingFieldByFieldRecursively_create_Test {
@@ -97,4 +99,20 @@ public void should_use_unambiguous_fields_description_when_standard_description_
                                          toHexString(withLinkedHashMap.map.hashCode())));
   }
 
+  @Test
+  public void should_not_fall_with_npe_if_field_of_one_of_compared_objects_is_null() {
+    final Name actualName = new Name("Andy");
+    final Name nullName = new Name(null);
+
+    Throwable error = ThrowableAssert.catchThrowable(new ThrowableAssert.ThrowingCallable() {
+      @Override
+      public void call() throws Throwable {
+        assertThat(actualName).isEqualToComparingFieldByFieldRecursively(nullName);
+      }
+    });
+
+    assertThat(error).isNotExactlyInstanceOf(NullPointerException.class);
+
+  }
+
 }