Skip to content

Commit ccdd3ba

Browse files
Merge branch 'recursive-api'
2 parents 2b22839 + 9dabbbf commit ccdd3ba

File tree

63 files changed

+6363
-28
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+6363
-28
lines changed

pom.xml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@
105105
<version>25.0-jre</version>
106106
<scope>test</scope>
107107
</dependency>
108+
<dependency>
109+
<groupId>org.apache.commons</groupId>
110+
<artifactId>commons-lang3</artifactId>
111+
<version>3.8.1</version>
112+
<scope>test</scope>
113+
</dependency>
108114
</dependencies>
109115
<build>
110116
<plugins>
@@ -217,6 +223,13 @@
217223
</execution>
218224
</executions>
219225
</plugin>
226+
<plugin>
227+
<artifactId>maven-javadoc-plugin</artifactId>
228+
<configuration>
229+
<!-- some jdk don't support nested li tags :( -->
230+
<doclint>none</doclint>
231+
</configuration>
232+
</plugin>
220233
<!-- generate jacoco report -->
221234
<plugin>
222235
<groupId>org.jacoco</groupId>
@@ -329,7 +342,8 @@
329342
</activation>
330343
<properties>
331344
<sonar.skip>true</sonar.skip>
332-
<!-- currently fails with "javadoc: error - The code being documented uses modules but the packages defined in https://docs.oracle.com/javase/8/docs/api/ are in the unnamed module." -->
345+
<!-- currently fails with "javadoc: error - The code being documented uses modules but the packages defined in https://docs.oracle.com/javase/8/docs/api/
346+
are in the unnamed module." -->
333347
<maven.javadoc.skip>true</maven.javadoc.skip>
334348
</properties>
335349
</profile>

src/main/java/org/assertj/core/api/AbstractAssert.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,14 @@ public abstract class AbstractAssert<SELF extends AbstractAssert<SELF, ACTUAL>,
6666
Conditions conditions = Conditions.instance();
6767

6868
@VisibleForTesting
69-
public final WritableAssertionInfo info;
69+
public WritableAssertionInfo info;
7070

7171
// visibility is protected to allow us write custom assertions that need access to actual
7272
@VisibleForTesting
7373
protected final ACTUAL actual;
7474
protected final SELF myself;
7575

76+
// = ConfigurationProvider.CONFIGURATION_PROVIDER.representation(); ?
7677
private static Representation customRepresentation = null;
7778

7879
@VisibleForTesting
@@ -675,9 +676,9 @@ public SELF satisfies(Consumer<ACTUAL> requirements) {
675676
* <p>
676677
* This allows users to perform <b>OR like assertions</b> since only one the assertions group has to be met.
677678
* <p>
678-
* {@link #overridingErrorMessage(String, Object...) Overriding error message} is not supported as it would prevent from
679+
* {@link #overridingErrorMessage(String, Object...) Overriding error message} is not supported as it would prevent from
679680
* getting the error messages of the failing assertions, these are valuable to figure out what went wrong.<br>
680-
* Describing the assertion is supported (for example with {@link #as(String, Object...)}).
681+
* Describing the assertion is supported (for example with {@link #as(String, Object...)}).
681682
* <p>
682683
* Example:
683684
* <pre><code class='java'> TolkienCharacter frodo = new TolkienCharacter("Frodo", HOBBIT);
@@ -710,9 +711,9 @@ public SELF satisfiesAnyOf(Consumer<ACTUAL> assertions1, Consumer<ACTUAL> assert
710711
* <p>
711712
* This allows users to perform <b>OR like assertions</b> since only one the assertions group has to be met.
712713
* <p>
713-
* {@link #overridingErrorMessage(String, Object...) Overriding error message} is not supported as it would prevent from
714+
* {@link #overridingErrorMessage(String, Object...) Overriding error message} is not supported as it would prevent from
714715
* getting the error messages of the failing assertions, these are valuable to figure out what went wrong.<br>
715-
* Describing the assertion is supported (for example with {@link #as(String, Object...)}).
716+
* Describing the assertion is supported (for example with {@link #as(String, Object...)}).
716717
* <p>
717718
* Example:
718719
* <pre><code class='java'> TolkienCharacter frodo = new TolkienCharacter("Frodo", HOBBIT);

src/main/java/org/assertj/core/api/AbstractObjectAssert.java

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import java.util.function.Function;
2727
import java.util.stream.Stream;
2828

29+
import org.assertj.core.annotations.Beta;
30+
import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
2931
import org.assertj.core.description.Description;
3032
import org.assertj.core.groups.Tuple;
3133
import org.assertj.core.internal.TypeComparators;
@@ -672,7 +674,7 @@ public AbstractListAssert<?, List<? extends Object>, Object, ObjectAssert<Object
672674
}
673675

674676
/**
675-
* Asserts that the object under test (actual) is equal to the given object based on recursive a property/field by property/field comparison (including
677+
* Asserts that the object under test (actual) is equal to the given object based on a recursive property/field by property/field comparison (including
676678
* inherited ones). This can be useful if actual's {@code equals} implementation does not suit you.
677679
* The recursive property/field comparison is <b>not</b> applied on fields having a custom {@code equals} implementation, i.e.
678680
* the overridden {@code equals} method will be used instead of a field by field comparison.
@@ -770,6 +772,74 @@ public <T> SELF returns(T expected, Function<ACTUAL, T> from) {
770772
return myself;
771773
}
772774

775+
/**
776+
* Enable using a recursive field by field comparison strategy when calling the chained {@link RecursiveComparisonAssert#isEqualTo(Object) isEqualTo} assertion.
777+
* <p>
778+
* Example:
779+
* <pre><code class='java'> public class Person {
780+
* String name;
781+
* double height;
782+
* Home home = new Home();
783+
* }
784+
*
785+
* public class Home {
786+
* Address address = new Address();
787+
* Date ownedSince;
788+
* }
789+
*
790+
* public static class Address {
791+
* int number;
792+
* String street;
793+
* }
794+
*
795+
* Person sherlock = new Person("Sherlock", 1.80);
796+
* sherlock.home.ownedSince = new Date(123);
797+
* sherlock.home.address.street = "Baker Street";
798+
* sherlock.home.address.number = 221;
799+
*
800+
* Person sherlock2 = new Person("Sherlock", 1.80);
801+
* sherlock2.home.ownedSince = new Date(123);
802+
* sherlock2.home.address.street = "Baker Street";
803+
* sherlock2.home.address.number = 221;
804+
*
805+
* // assertion succeeds as the data of both objects are the same.
806+
* assertThat(sherlock).usingRecursiveComparison()
807+
* .isEqualTo(sherlock2);
808+
*
809+
* // assertion fails because sherlock.equals(sherlock2) is false.
810+
* assertThat(sherlock).isEqualTo(sherlock2);</code></pre>
811+
* <p>
812+
* The recursive comparison is performed according to the default {@link RecursiveComparisonConfiguration} that is:
813+
* <ul>
814+
* <li>actual and expected objects and their fields were compared field by field recursively even if they were not of the same type, this allows for example to compare a Person to a PersonDto (call {@link RecursiveComparisonAssert#withStrictTypeChecking() withStrictTypeChecking()} to change that behavior). </li>
815+
* <li>overridden equals methods were used in the comparison (unless stated otherwise)</li>
816+
* <li>these types were compared with the following comparators: </li>
817+
* <ul>
818+
* <li>java.lang.Double -&gt; DoubleComparator[precision=1.0E-15] </li>
819+
* <li>java.lang.Float -&gt; FloatComparator[precision=1.0E-6] </li>
820+
* <li>any comparators previously registered with {@link AbstractObjectAssert#usingComparatorForType(Comparator, Class)} </li>
821+
* </ul>
822+
* </ul>
823+
*
824+
* @return a new {@link RecursiveComparisonAssert} instance
825+
*/
826+
@Beta
827+
public RecursiveComparisonAssert<?> usingRecursiveComparison() {
828+
return usingRecursiveComparison(new RecursiveComparisonConfiguration());
829+
}
830+
831+
/**
832+
* Same as {@link #usingRecursiveComparison()} but allows to specify your own {@link RecursiveComparisonConfiguration}.
833+
* @param recursiveComparisonConfiguration the {@link RecursiveComparisonConfiguration} used in the chained {@link RecursiveComparisonAssert#isEqualTo(Object) isEqualTo} assertion.
834+
*
835+
* @return a new {@link RecursiveComparisonAssert} instance built with the given {@link RecursiveComparisonConfiguration}.
836+
*/
837+
@Beta
838+
public RecursiveComparisonAssert<?> usingRecursiveComparison(RecursiveComparisonConfiguration recursiveComparisonConfiguration) {
839+
return new RecursiveComparisonAssert<>(actual, recursiveComparisonConfiguration).withAssertionState(myself)
840+
.withTypeComparators(comparatorByType);
841+
}
842+
773843
// override for proxyable friendly AbstractObjectAssert
774844
protected AbstractObjectAssert<?, ?> newObjectAssert(Object objectUnderTest) {
775845
return new ObjectAssert<>(objectUnderTest);

src/main/java/org/assertj/core/api/Assumptions.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import java.util.stream.Stream;
6868

6969
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
70+
import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
7071
import org.assertj.core.util.CheckReturnValue;
7172

7273
import net.bytebuddy.ByteBuddy;
@@ -1181,11 +1182,19 @@ private static RuntimeException assumptionNotMet(Class<?> exceptionClass,
11811182
if (assertion instanceof MapSizeAssert) return asMapSizeAssumption(assertion);
11821183
if (assertion instanceof ProxyableObjectAssert) return asAssumption(ObjectAssert.class, Object.class, actual);
11831184
if (assertion instanceof ObjectAssert) return asAssumption(ObjectAssert.class, Object.class, actual);
1185+
if (assertion instanceof RecursiveComparisonAssert) return asRecursiveComparisonAssumption(assertion);
11841186
// @format:on
11851187
// should not arrive here
11861188
throw new IllegalArgumentException("Unsupported assumption creation for " + assertion.getClass());
11871189
}
11881190

1191+
private static AbstractAssert<?, ?> asRecursiveComparisonAssumption(AbstractAssert<?, ?> assertion) {
1192+
RecursiveComparisonAssert<?> recursiveComparisonAssert = (RecursiveComparisonAssert<?>) assertion;
1193+
RecursiveComparisonConfiguration recursiveComparisonConfiguration = recursiveComparisonAssert.getRecursiveComparisonConfiguration();
1194+
Class<?>[] constructorTypes = array(Object.class, RecursiveComparisonConfiguration.class);
1195+
return asAssumption(RecursiveComparisonAssert.class, constructorTypes, assertion.actual, recursiveComparisonConfiguration);
1196+
}
1197+
11891198
private static AbstractAssert<?, ?> asMapSizeAssumption(AbstractAssert<?, ?> assertion) {
11901199
MapSizeAssert<?, ?> mapSizeAssert = (MapSizeAssert<?, ?>) assertion;
11911200
Class<?>[] constructorTypes = array(AbstractMapAssert.class, Integer.class);

src/main/java/org/assertj/core/api/ProxifyMethodChangingTheObjectUnderTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,14 @@ public class ProxifyMethodChangingTheObjectUnderTest {
5050
private AbstractAssert createAssertProxy(Object currentActual) {
5151
if (currentActual instanceof IterableSizeAssert) return createIterableSizeAssertProxy(currentActual);
5252
if (currentActual instanceof MapSizeAssert) return createMapSizeAssertProxy(currentActual);
53-
return (AbstractAssert) proxies.createSoftAssertionProxy(currentActual.getClass(), actualClass(currentActual), actual(currentActual));
53+
if (currentActual instanceof RecursiveComparisonAssert)
54+
return createRecursiveComparisonAssertProxy((RecursiveComparisonAssert) currentActual);
55+
return (AbstractAssert) proxies.createSoftAssertionProxy(currentActual.getClass(), actualClass(currentActual),
56+
actual(currentActual));
57+
}
58+
59+
private RecursiveComparisonAssert<?> createRecursiveComparisonAssertProxy(RecursiveComparisonAssert<?> currentAssert) {
60+
return proxies.createRecursiveComparisonAssertProxy(currentAssert);
5461
}
5562

5663
private MapSizeAssert<?, ?> createMapSizeAssertProxy(Object currentActual) {
@@ -72,6 +79,7 @@ private static Class actualClass(Object result) {
7279
if (result instanceof ObjectAssert || result instanceof ProxyableObjectAssert) return Object.class;
7380
if (result instanceof MapAssert) return Map.class;
7481
if (result instanceof StringAssert) return String.class;
82+
if (result instanceof RecursiveComparisonAssert) return Object.class;
7583
// Trying to create a proxy will only match exact constructor argument types.
7684
// To initialize one for ListAssert for example we can't use an ArrayList, we have to use a List.
7785
// So we can't just return actual.getClass() as we could read a concrete class whereas

0 commit comments

Comments
 (0)