Skip to content

Commit

Permalink
add AbstractObjectAssert#usingComparatorForType(comparator, class) (c…
Browse files Browse the repository at this point in the history
…loses #198, closes #585)
  • Loading branch information
PascalSchumacher authored and joel-costigliola committed Feb 27, 2016
1 parent 1fb35f5 commit fb9c509
Show file tree
Hide file tree
Showing 13 changed files with 332 additions and 121 deletions.
102 changes: 85 additions & 17 deletions src/main/java/org/assertj/core/api/AbstractObjectAssert.java
Expand Up @@ -38,7 +38,8 @@
*/
public abstract class AbstractObjectAssert<S extends AbstractObjectAssert<S, A>, A> extends AbstractAssert<S, A> {

private Map<String, Comparator<?>> propertyOrFieldToComparator = new HashMap<>();
private Map<String, Comparator<?>> comparatorByPropertyOrField = new HashMap<>();
private Map<Class<?>, Comparator<?>> comparatorByType = new HashMap<>();

protected AbstractObjectAssert(A actual, Class<?> selfType) {
super(actual, selfType);
Expand Down Expand Up @@ -81,7 +82,7 @@ protected AbstractObjectAssert(A actual, Class<?> selfType) {
* @throws IntrospectionError if one of actual's field to compare can't be found in the other object.
*/
public S isEqualToIgnoringNullFields(Object other) {
objects.assertIsEqualToIgnoringNullFields(info, actual, other, propertyOrFieldToComparator);
objects.assertIsEqualToIgnoringNullFields(info, actual, other, comparatorByPropertyOrField, comparatorByType);
return myself;
}

Expand Down Expand Up @@ -123,7 +124,8 @@ public S isEqualToIgnoringNullFields(Object other) {
* @throws IntrospectionError if a property/field does not exist in actual.
*/
public S isEqualToComparingOnlyGivenFields(Object other, String... propertiesOrFieldsUsedInComparison) {
objects.assertIsEqualToComparingOnlyGivenFields(info, actual, other, propertyOrFieldToComparator, propertiesOrFieldsUsedInComparison);
objects.assertIsEqualToComparingOnlyGivenFields(info, actual, other, comparatorByPropertyOrField, comparatorByType,
propertiesOrFieldsUsedInComparison);
return myself;
}

Expand Down Expand Up @@ -161,7 +163,8 @@ public S isEqualToComparingOnlyGivenFields(Object other, String... propertiesOrF
* @throws IntrospectionError if one of actual's property/field to compare can't be found in the other object.
*/
public S isEqualToIgnoringGivenFields(Object other, String... propertiesOrFieldsToIgnore) {
objects.assertIsEqualToIgnoringGivenFields(info, actual, other, propertyOrFieldToComparator, propertiesOrFieldsToIgnore);
objects.assertIsEqualToIgnoringGivenFields(info, actual, other, comparatorByPropertyOrField, comparatorByType,
propertiesOrFieldsToIgnore);
return myself;
}

Expand Down Expand Up @@ -199,46 +202,111 @@ public S isEqualToIgnoringGivenFields(Object other, String... propertiesOrFields
* @throws IntrospectionError if one of actual's property/field to compare can't be found in the other object.
*/
public S isEqualToComparingFieldByField(Object other) {
objects.assertIsEqualToIgnoringGivenFields(info, actual, other, propertyOrFieldToComparator);
objects.assertIsEqualToIgnoringGivenFields(info, actual, other, comparatorByPropertyOrField, comparatorByType);
return myself;
}

/**
* Allows to define a comparator which is used to compare the values of properties or fields with the given names.
*
* A typical usage is for comparing double/float fields with a given precision.
* <p>
* Comparators added by this method have precedence on comparators added by {@link #usingComparatorForType}.
* <p>
* Example:
* <p>
* <pre><code class='java'> public class TolkienCharacter {
* private String name;
* private int age;
* private double size;
* // constructor omitted
* }
* TolkienCharacter frodo = new TolkienCharacter(&quot;Frodo&quot;, 33);
* TolkienCharacter olderFrodo = new TolkienCharacter(&quot;Frodo&quot;, 66);
* TolkienCharacter frodo = new TolkienCharacter(&quot;Frodo&quot;, 1.2);
* TolkienCharacter tallerFrodo = new TolkienCharacter(&quot;Frodo&quot;, 1.3);
* TolkienCharacter reallyTallFrodo = new TolkienCharacter(&quot;Frodo&quot;, 1.9);
*
* Comparator&lt;Integer&gt; alwaysEqual = new Comparator&lt;Integer&gt;() {
* public int compare(Integer o1, Integer o2) {
* return 0;
* Comparator&lt;Double&gt; closeEnough = new Comparator&lt;Double&gt;() {
* double precision = 0.5;
* public int compare(Double d1, Double d2) {
* return Math.abs(d1 -d2) <= precision ? 0 : 1;
* }
* };
*
* // assertions will pass
* assertThat(frodo).usingComparatorForFields(alwaysEqual, &quot;age&quot;).isEqualToComparingFieldByField(olderFrodo);
* assertThat(frodo).usingComparatorForFields(alwaysEqual, &quot;age&quot;).isEqualToIgnoringNullFields(olderFrodo);
* assertThat(frodo).usingComparatorForFields(alwaysEqual, &quot;age&quot;).isEqualToIgnoringGivenFields(olderFrodo, &quot;name&quot;);
* assertThat(frodo).usingComparatorForFields(alwaysEqual, &quot;age&quot;).isEqualToComparingOnlyGivenFields(olderFrodo, &quot;age&quot;);</code></pre>
* assertThat(frodo).usingComparatorForFields(closeEnough, &quot;size&quot;)
* .isEqualToComparingFieldByField(tallerFrodo);
*
* assertThat(frodo).usingComparatorForFields(closeEnough, &quot;size&quot;)
* .isEqualToIgnoringNullFields(tallerFrodo);
*
* assertThat(frodo).usingComparatorForFields(closeEnough, &quot;size&quot;)
* .isEqualToIgnoringGivenFields(tallerFrodo);
*
* assertThat(frodo).usingComparatorForFields(closeEnough, &quot;size&quot;)
* .isEqualToComparingOnlyGivenFields(tallerFrodo);
*
* // assertion will fail
* assertThat(frodo).usingComparatorForFields(closeEnough, &quot;size&quot;)
* .isEqualToComparingFieldByField(reallyTallFrodo);</code></pre>
* </p>
* @param comparator the {@link java.util.Comparator} to use
* @param comparator the names of the properties and/or fields the comparator should be used for
* @return {@code this} assertions object
*/
public <T> S usingComparatorForFields(Comparator<T> comparator, String... propertiesOrFields) {
for (String propertyOrField : propertiesOrFields) {
propertyOrFieldToComparator.put(propertyOrField, comparator);
comparatorByPropertyOrField.put(propertyOrField, comparator);
}
return myself;
}

/**
* Allows to define a comparator which is used to compare the values of properties or fields with the given type.
* A typical usage is for comparing fields of numeric type at a given precision.
* <p>
* Comparators added by {@link #usingComparatorForFields} have precedence on comparators added by this method.
* <p>
* Example:
* <pre><code class='java'> public class TolkienCharacter {
* private String name;
* private double size;
* // constructor omitted
* }
* TolkienCharacter frodo = new TolkienCharacter(&quot;Frodo&quot;, 1.2);
* TolkienCharacter tallerFrodo = new TolkienCharacter(&quot;Frodo&quot;, 1.3);
* TolkienCharacter reallyTallFrodo = new TolkienCharacter(&quot;Frodo&quot;, 1.9);
*
* Comparator&lt;Double&gt; closeEnough = new Comparator&lt;Double&gt;() {
* double precision = 0.5;
* public int compare(Double d1, Double d2) {
* return Math.abs(d1 -d2) <= precision ? 0 : 1;
* }
* };
*
* // assertions will pass
* assertThat(frodo).usingComparatorForType(closeEnough, Double.class)
* .isEqualToComparingFieldByField(tallerFrodo);
*
* assertThat(frodo).usingComparatorForType(closeEnough, Double.class)
* .isEqualToIgnoringNullFields(tallerFrodo);
*
* assertThat(frodo).usingComparatorForType(closeEnough, Double.class)
* .isEqualToIgnoringGivenFields(tallerFrodo);
*
* assertThat(frodo).usingComparatorForType(closeEnough, Double.class)
* .isEqualToComparingOnlyGivenFields(tallerFrodo);
*
* // assertion will fail
* assertThat(frodo).usingComparatorForType(closeEnough, Double.class)
* .isEqualToComparingFieldByField(reallyTallFrodo);</code></pre>
* </p>
* @param comparator the {@link java.util.Comparator} to use
* @param comparator the {@link java.lang.Class} of the type the comparator should be used for
* @return {@code this} assertions object
*/
public <T> S usingComparatorForType(Comparator<T> comparator, Class<T> type) {
comparatorByType.put(type, comparator);
return myself;
}

/**
* Assert that the actual object has the specified field or property.
* <p/>
Expand Down
Expand Up @@ -12,7 +12,8 @@
*/
package org.assertj.core.internal;

import java.util.Collections;
import static java.util.Collections.EMPTY_MAP;

import java.util.Comparator;

import org.assertj.core.api.Assertions;
Expand All @@ -37,7 +38,7 @@ public int compare(Object actual, Object other) {
@SuppressWarnings("unchecked")
protected boolean areEqual(Object actual, Object other) {
try {
return Objects.instance().areEqualToIgnoringGivenFields(actual, other, Collections.EMPTY_MAP);
return Objects.instance().areEqualToIgnoringGivenFields(actual, other, EMPTY_MAP, EMPTY_MAP);
} catch (IntrospectionError e) {
return false;
}
Expand Down
Expand Up @@ -12,7 +12,7 @@
*/
package org.assertj.core.internal;

import java.util.Collections;
import static java.util.Collections.EMPTY_MAP;

import org.assertj.core.presentation.StandardRepresentation;
import org.assertj.core.util.VisibleForTesting;
Expand All @@ -36,7 +36,7 @@ public String[] getFields() {
@SuppressWarnings("unchecked")
protected boolean areEqual(Object actualElement, Object otherElement) {
try {
return Objects.instance().areEqualToIgnoringGivenFields(actualElement, otherElement, Collections.EMPTY_MAP, fields);
return Objects.instance().areEqualToIgnoringGivenFields(actualElement, otherElement, EMPTY_MAP, EMPTY_MAP, fields);
} catch (IntrospectionError e) {
return false;
}
Expand Down

0 comments on commit fb9c509

Please sign in to comment.