10

I have the following same strange situation into a JUnit test.

So I have this test method:

@Test
public void getNavInfoTest() throws ParseException {

    TirAliquotaRamoI expectedObject = new TirAliquotaRamoI();

    DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    Date date;

    date = formatter.parse("2015-08-01");

    Date dataInizio = formatter.parse("2015-08-01");
    Date dataFine = formatter.parse("2100-12-31");

    expectedObject.setDataElaborazione(date);
    expectedObject.setTassoLordoAnnuoAppl(BigDecimal.ZERO);
    expectedObject.setTassoGiornalieroNetto(BigDecimal.ZERO);
    expectedObject.setAliquota(BigDecimal.ONE);
    expectedObject.setDataInizio(dataInizio);
    expectedObject.setDataFine(dataFine);

    TirAliquotaRamoI tirAliquotaRamoI = pucManager.getNavInfo(date);

    assertEquals(tirAliquotaRamoI.getAliquota(), expectedObject.getAliquota());

}

At the end I am simply testing if the tirAliquotaRamoI.getAliquota() (obtained from a DB query) have the same value of the same field defined for the created expectedObject:

assertEquals(tirAliquotaRamoI.getAliquota(), expectedObject.getAliquota());

So the field for the expected object is created using the BigDecimal.ONE constand and using the debugger I can see that its value is 1.

And the tirAliquotaRamoI.getAliquota() obtaind value is 1.000000000

So, in theory, both represent the same value 1 but the test fail and I obtain:

java.lang.AssertionError: expected:<1.000000000> but was:<1>
    at org.junit.Assert.fail(Assert.java:88)
    at org.junit.Assert.failNotEquals(Assert.java:834)
    at org.junit.Assert.assertEquals(Assert.java:118)
    at org.junit.Assert.assertEquals(Assert.java:144)
    at com.fideuram.dbmanager.PucManagerTest.getNavInfoTest(PucManagerTest.java:90)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Why if both represent the ame 1.0 value? How can I fix this issue to pass the test?

xenteros
  • 15,586
  • 12
  • 56
  • 91
AndreaNobili
  • 40,955
  • 107
  • 324
  • 596

6 Answers6

15

The reason is how the BigDecimal equals is implemented. BigDecimal.ONE is constructed as new BigDecimal(BigInteger.ONE, 1, 0, 1). The equals method is implemented in the following way (from JDK source code):

public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    if (scale != xDec.scale)
        return false;
    long s = this.intCompact;
    long xs = xDec.intCompact;
    if (s != INFLATED) {
        if (xs == INFLATED)
            xs = compactValFor(xDec.intVal);
        return xs == s;
    } else if (xs != INFLATED)
        return xs == compactValFor(this.intVal);

    return this.inflated().equals(xDec.inflated());
}

So two BigDecimals are equal only if they have the same scale. ONEs scale is smaller than returned BigDecimal's so they're not equal.

I saw some answers saying that you can take floatValue of BigDecimal. Well, you can't. BigDecimal is used when dealing with bigger numbers so in this certain case it would work, but is a bad pattern.

But fortunately we can use compareTo method!

assertTrue(tirAliquotaRamoI.getAliquota().compareTo(expectedObject.getAliquota()) == 0);

It's not perfect but will work!

xenteros
  • 15,586
  • 12
  • 56
  • 91
10

BigDecimal comparison always uses the scale as well, therefore your tests are failing.

See Javadoc for details.

If you are interested only in the value and not the scale, then consider using stripTrailingZeros() when comparing.

assertEquals(
    tirAliquotaRamoI.getAliquota().stripTrailingZeros(),
    expectedObject.getAliquota().stripTrailingZeros()
);
xenteros
  • 15,586
  • 12
  • 56
  • 91
Jocce Nilsson
  • 1,658
  • 14
  • 28
7

It looks to me like they have different scales.

Try compareTo:

 assertEquals(0, tirAliquotaRamoI.compareTo(expectedObject.getAliquota());

Or if you care about matching the scales then you'll have to change the scale on either object using setScale(int newScale) to match the other.

Hope that helps.

Thanks to @David SN for the correction.

Geraint
  • 71
  • 1
  • 4
  • The problem with this approach is that when the test fails you won't know why, as in you'll get no diagnostics of what the actual value is – tddmonkey Aug 26 '16 at 10:25
  • 2
    `assertTrue` has a boolean parameter. You should use `assertTrue(tirAliquotaRamoI.compareTo(expectedObject.getAliquota() == 0);` or `assertEquals(0, tirAliquotaRamoI.compareTo(expectedObject.getAliquota());` – David SN Aug 26 '16 at 10:28
  • or using stripTrailingZeroes() as in my example – Jocce Nilsson Aug 26 '16 at 10:29
  • tddmonkey: But isn't this test just to ensure that both values are equal? DavidSN: I can't believe I missed that, thanks I'll edit the answer. – Geraint Aug 26 '16 at 10:31
4

Although I don't know JUnit methods, I guess the problem relates to:

public boolean equals(Object x)

Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).

(see BigDecimal.equals() docs)

Probably, assertEquals() is using the aforementioned method, which compares both the value and the scale of your numbers.

You may want to try an approach with assertTrue() as suggested in x870eaddd's answer

Community
  • 1
  • 1
watery
  • 5,026
  • 9
  • 52
  • 92
0

Try this :

assertEquals((int)tirAliquotaRamoI.getAliquota(), (int)expectedObject.getAliquota());

I think AssertEquals can only compare two variable from same type (int to int, double to double...). In your test, one is a decimal and the other is not, so it probably can't compare them.

xenteros
  • 15,586
  • 12
  • 56
  • 91
Lexo
  • 95
  • 2
0

What is data type for tirAliquotaRamoI.getAliquota()? Is it BigDecimal too because BigDecimal.ONE is a BigDecimal data type.

If tirAliquotaRamoI.getAliquota() is Long or Int or Float, you can use

assertEquals(tirAliquotaRamoI.getAliquota(), BigDecimal.ONE.intValue())

or

assertEquals(tirAliquotaRamoI.getAliquota(), BigDecimal.ONE.longValue())

xenteros
  • 15,586
  • 12
  • 56
  • 91
Nghia Do
  • 2,588
  • 2
  • 17
  • 31