Re: Asserts using Comparable.compareTo() instead of equals() (for BigDecimal and others)

Kevin Cooney <[email protected]> Wed, 12 Mar 2014 19:23:58 -0700
Newsgroups gmane.comp.java.junit.user
Message-ID <CAA3E+eUOba7WzqVOA0Gxo0kSiDLTGysvTzijODk0+HNwAfFx6A@mail.gmail.com>
When this came up last time, we had a long discussion, and finally decided
that developers should use Hamcrest:
https://github.com/junit-team/junit/issues/95

I am not personally opposed to finding a simple way to solve this in JUnit;
just wanted to point people to the previous threads.
 On Mar 12, 2014 6:14 PM, <[email protected]> wrote:

>
>
> 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);
>         }
>     }
>
>  
>