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