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?
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. ONE
s 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!
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()
);
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With