Asserts using Comparable.compareTo() instead of equals() (for BigDecimal and others)
<[email protected]> 12 Mar 2014 17:19:38 -0700
| Newsgroups | gmane.comp.java.junit.user |
|---|---|
| Message-ID | <[email protected]> |
Comrades,
Comparing two BigDecimals for the same value shouldn't be done with the equals() method, since two instances that have the same value but a different scale won't be "equal." But there aren't any methods in Assert.class to do this. This means I need to write this:
Assert.assertTrue(expected.compareTo(actual) == 0);
when I would rather write this:
Assert.assertCompare(expected, actual)
The second one has the added advantage that it can throw a ComparisonFailure, so I can see the two values in the test results.
I would like to suggest the following four methods be added to Assert.class:
<T extends Comparable<T> void assertCompare(T, T);
<T extends Comparable<T> void assertCompare(String, T, T)
<T> void assertCompare(T, T, Comparator<T>)
<T> void assertCompare(String, T, T, Comparator<T>)
I have some code that implements these methods. I have this working in a production environment. I use it on BigDecimal values, but it works with any values that implement Comparable, or anything for which you can write a Comparator.
Here is my proposed code:
/**
* Compare two Comparable objects using Comparable.compareTo() instead
* of equals(). This is most useful for classes for which Comparable is
* inconsistent with equals(), such as BigDecimal.
*
* @param expected
* expected value
* @param actual
* actual value
* @param <T>
* Any object that implements Comparable
* @see java.lang.Comparable
*/
public static <T extends Comparable<T>> void assertCompare(final T expected, final T actual) {
assertCompare(null, expected, actual);
}
/**
* Compare two Comparable objects using Comparable.compareTo() instead
* of equals(). This is primarily for classes for which Comparable is
* inconsistent with equals(), such as BigDecimal.
*
* @param message
* the identifying message for the {@link AssertionError} (<code>null</code>
* okay)
* @param expected
* expected value
* @param actual
* actual value
* @param <T>
* Any object that implements Comparable
* @see java.lang.Comparable
*/
public static <T extends Comparable<T>> void assertCompare(final String message, final T expected, final T actual) {
if (expected == null && actual == null) {
// okay
} else if (expected != null && actual != null && expected.compareTo(actual) == 0) {
// We tested both for null here, because the compareTo method might
// not handle a null parameter cleanly. (BigDecimal, for example,
// throws a NullPointerException.) For these cases, it's
// preferable to throw a ComparisonFailure exception.
} else {
failCompare(message, expected, actual);
}
}
private static void failCompare(final String message, final Object expected, final Object actual) {
String cleanMessage = message == null ? "" : message;
String expectedString = expected == null? null : expected.toString();
String actualString = actual == null? null : actual.toString();
throw new ComparisonFailure(cleanMessage, expectedString, actualString);
}
/**
* Compare two objects using Comparator.compare() instead of equals().
* This is primarily for classes with a natural order that is
* inconsistent with equals(), such as BigDecimal.
*
* @param message
* the identifying message for the {@link AssertionError} (<code>null</code>
* okay)
* @param expected
* expected value
* @param actual
* actual value
* @param comparator
* The comparator used to compare the values
* @param <T>
* The type of the object
* @see java.util.Comparator
*/
public static <T> void assertCompare(final T expected, final T actual, final Comparator<T> comparator) {
assertCompare(null, expected, actual, comparator);
}
/**
* Compare two objects using Comparator.compare() instead of equals().
* This is primarily for classes with a natural order that is
* inconsistent with equals(), such as BigDecimal.
*
* @param message
* the identifying message for the {@link AssertionError} (<code>null</code>
* okay)
* @param expected
* expected value
* @param actual
* actual value
* @param comparator
* The comparator used to compare the values
* @param <T>
* The type of the object
* @see java.lang.Comparator
*/
public static <T> void assertCompare(final String message, final T expected, final T actual, final Comparator<T> comparator) {
if (comparator == null) {
throw new IllegalArgumentException("Null Comparator in test");
}
if (expected == null && actual == null) {
// okay
} else if (expected != null && actual != null && comparator.compare(actual, expected) == 0) {
// We tested both for null here, because the compare() method might
// not handle a null parameter cleanly. For these cases, it's
// preferable to throw a ComparisonFailure exception.
} else {
failCompare(message, expected, actual);
}
}