Junit difference between assertEquals(Double, Double) and assertEquals(double, double, delta)

StackOverflow https://stackoverflow.com/questions/11242224

  •  17-06-2021
  •  | 
  •  

Question

I had a junit test asserting two Double objects with the following:

Assert.assertEquals(Double expected, Double result);

This was was fine then I decided to change it to use the primitive double instead which turned out to be deprecated unless you also provide a delta.

so what I am wondering is what is the difference between using the Double object or the primitive type in this assertEquals? Why is using the objects without a delta ok but then using the primitives without a delta is deprecated? Is Java doing something in the background which already has a default delta value taken into account?

Thanks.

Was it helpful?

Solution

There is NO assert method in JUnit with the signature

assertEquals(Double expected, Double result);

There is one, however, generic for objects:

assertEquals(Object expected, Object result);

This calls the objects' equals method and as you can expect, it is not recommended to use this for comparing Double objects.

For doubles, as you observed, it is absolutely necessary to use a delta for comparison, to avoid issues with floating-point rounding (explained already in some other answers). If you use the 3-argument version of assertEquals with double arguments

assertEquals(double expected, double actual, double delta);

your Doubles will get silently unboxed to double and everything will work fine (and your tests won't fail unexpectedly :-).

OTHER TIPS

Double math rarely if ever gives exactly equal results. For example, 0.1 * 0.1 != 0.01. You usually need at least some delta in comparing double-precision results.

On the other hand, if you're comparing boxed Doubles, it assumes you want the exact equality. Java doesn't have a default delta value taken into account, but Double.equals has slightly different behavior from ==: in particular, its handling of NaNs.

This makes sense in testing, because Double.NaN != Double.NaN, but in a test, if you expected an NaN and NaN was returned, that's a correct answer.

Better write something like this:

assertEquals(23.0, 250.0, 0.0)  

0.0 - it is delta. Read why yours methods are deprecated.

SOURCE. Asserts that two doubles or floats are equal to within a positive delta. If they are not, an AssertionError is thrown. If the expected value is infinity then the delta value is ignored.NaNs are considered equal.

I'd say that comparing doubles, primitive or object, is useless without a delta. Knowing how flowing point numbers work is key to doing numerical work.

The object might be using .equals under the covers; the primitive has no option besides ==.

Just because the object version isn't using a delta doesn't make that a better idea.

I'm answering to update regarding JUnit 5. Regardless of the version, assertEquals(Double expected, Double actual) gets routed to assertEquals(Object expected, Object actual), because, as others already explained, Double is a primitive wrapper that implicitly extends Object.

But assertEquals(double expected, double actual) (with two primitives rather than two primitive wrappers) in a JUnit 4 context calls a deprecated procedure. I don't know if it was already deprecated in JUnit 3. But guess what: it's not deprecated in JUnit 5.

Floating point types like double are inherently imprecise. Suppose expected = 0.3. How is actual computed? If it's 3.0 / 10 the result might be exact. But if it's the infamous 0.1 + 0.2, it will be off by 0.00000000000000004 (which somehow becomes 5.551115123125783 × 10−17 if you subtract 0.3).

Generally speaking, floating point multiplication and division are more reliable than floating point addition and subtraction.

The following example comes from a test class with an org.junit.jupiter.api.Assertions.* static import.

    @Test
    void testDeltaExample() {
        double expected = 0.3;
        double actual = 0.1 + 0.2;
        assertEquals(expected, actual);
    }

Result:

org.opentest4j.AssertionFailedError: expected: <0.3> but was: <0.30000000000000004> at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55) at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:70) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:65) at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:868) ...

Do you care if it's off by a tiny amount like that? If you don't, then you'll want a delta like 0.0000000000000001.

You can still use "vintage" JUnit from JUnit 5, if you want (but you shouldn't want to for any reason other than that you're migrating existing test suites, or to see that it can be done).

    @Test
    void testDeltaExample() {
        double expected = 0.3;
        double actual = 0.1 + 0.2;
        org.junit.Assert.assertEquals(expected, actual);
    }

(your IDE should show a strike-through in the line before the closing brace). This test will fail even if expected and actual have the exact same bit pattern (which they won't in this example).

java.lang.AssertionError: Use assertEquals(expected, actual, delta) to compare floating-point numbers at org.junit.Assert.fail(Assert.java:88) at org.junit.Assert.assertEquals(Assert.java:667) at org.junit.Assert.assertEquals(Assert.java:656) ...

Many quite intelligent Java programmers who have learned such complicated things as SQL joins simply didn't care to learn about floating point numbers, so they just used a delta of 0.0.

And so it seems that the JUnit developers decided it wasn't their job to educate people on the foibles of floating point. So org.junit.jupiter.api.Assertions.assertEquals(double expected, double actual) is technically new and not at all deprecated.

To be absolutely clear, floating point comparison with a delta is still available in JUnit 5. From a Scala perspective, you can regard it as assertEquals(expected: Double, actual: Double, delta: Double = 0.0).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top