73

I want to use assert between 2 two decimal, I use this:

BigDecimal bd1 = new BigDecimal (1000);
BigDecimal bd2 = new BigDecimal (1000);
org.junit.Assert.assertSame (bd1,bd2);

but the JUnit log shows:

expected <1000> was not: <1000>
Tunaki
  • 132,869
  • 46
  • 340
  • 423
kAnGeL
  • 743
  • 1
  • 5
  • 8
  • 2
    They are not the same object as expected. You might be looking to check whether they are `equals` Note: BigDecimal doesn't consider `1000.0` and `1000.00` as equal as the number of decimal places are different. IMHO `double` is simpler and no more error prone. ;) – Peter Lawrey Feb 23 '16 at 09:26
  • @PeterLawrey I wonder when will that be fixed, I really can't how is this useful in any scenario. – Maroun Feb 23 '16 at 09:29
  • 1
    @MarounMaroun For backward compatibility, it will never be fixed. – Peter Lawrey Feb 23 '16 at 09:33
  • @kAnGeL can you please revisit the ansers and chose the most correct and appropriate ? – frhack May 26 '20 at 05:51

11 Answers11

132

The official junit solution to assert that two BigDecimal are matematically equal is to use hamcrest.

With java-hamcrest 2.0.0.0 we can use this syntax:

    // import static org.hamcrest.MatcherAssert.assertThat;
    // import org.hamcrest.Matchers;

    BigDecimal a = new BigDecimal("100")
    BigDecimal b = new BigDecimal("100.00")
    assertThat(a,  Matchers.comparesEqualTo(b));

Hamcrest 1.3 Quick Reference

JonyD
  • 1,237
  • 3
  • 21
  • 34
frhack
  • 4,862
  • 2
  • 28
  • 25
  • 6
    This also gives meaningful error message compared to accepted and highest-voted answer: "java.lang.AssertionError: Expected: a value equal to <0.22> but: <0.222> was greater than <0.22>" – zbstof Jul 26 '17 at 18:34
  • 1
    No idea why this doesn't work for me. Some kind of casting / generics weirdness. `java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V` – Stewart Aug 24 '18 at 15:25
47

assertSamechecks if both objects are the same instance. assertEqualschecks if the numbers are equal in value and scale, that means i.e. 1000 is not equal to 1000.00. If you want to compare only the numeric value, you should use compareTo() method from BigDecimal.

For example:

BigDecimal bd1 = new BigDecimal (1000.00);
BigDecimal bd2 = new BigDecimal (1000);
org.junit.Assert.assertTrue(bd1.compareTo(bd2) == 0); 
Endery
  • 1,090
  • 17
  • 31
  • 4
    `comparesEqualTo()` is preferable to this because in failures, `assertThat(x.compareTo(y), is(y)` gives something like `Expected: is <0> but: was <1>`. `assertThat(x, comparesEqualTo(y)` gives `Expected: a value equal to <27700> but: <6700.000> was less than <27700>` – slim Nov 14 '17 at 13:12
  • 3
    @slim Yes, the answer with Hamcrest gives a more meaningful error message. My answer on the other hand is not dependent from Hamcrest and explains the logic of why the accepted answer can fail. – Endery Nov 14 '17 at 13:43
17

The answer to the question was already given. But some of the answers answer another question, namely 'how to compare 2 BigDecimals?'. The given solutions so far are either wrong or obsolete. I would like to suggest to try this:

// import static org.assertj.core.api.Assertions.assertThat;

BigDecimal a = new BigDecimal("100")
BigDecimal b = new BigDecimal("100.00")
assertThat(a).isEqualByComparingTo(b);
Sweder Schellens
  • 410
  • 1
  • 4
  • 14
  • @roma2341 Could you please explain what exactly is not working? Perhaps provide a code example? – Sweder Schellens Aug 08 '21 at 03:52
  • 1
    Method isEqualByComparingTo() use BigDecimal.compareTo() which is recommended to use. It is close to assertTrue(bd1.compareTo(bd2) == 0) but in assertThat(a).isEqualByComparingTo(b) you see expected value not just place of fault. – Mike Menko Aug 09 '21 at 11:40
  • 2
    This is the correct answer, thank you. In 2021 we shouldn't be using compareTo==0 directly, like cavemen. – Steve Oct 08 '21 at 16:48
16

Comparing BigDecimal with compareTo() works (as in: it ignore the scale and compare the actual number) but when unit testing it's useful to know what's the actual number, specially when the test fail.

An option I've used in this case is stripTrailingZeros() on both BigDecimal:

assertEquals(new BigDecimal("150").stripTrailingZeros(),
                    otherBigDecimal.stripTrailingZeros());

What this function does is remove zeroes without changing the number, so "150" is converted in "1.5E+2": this way it doesn't matter if you have 150, 150.00 or other form in otherBigDecimal because they get normalized into the same form.

The only difference is a null in otherBigDecimal would give a NullPointerException instead of an assertion error.

Daniele Segato
  • 12,314
  • 6
  • 62
  • 88
9

assertSame tests that the two objects are the same objects, i.e. that they are ==:

Asserts that two objects refer to the same object. If they are not the same, an AssertionError without a message is thrown.

In your case, since bd1 and bd2 are both new BigDecimal, the objects aren't the same, hence the exception.

What you want is to use assertEquals, that tests if two objects are equal, i.e. .equals:

Asserts that two objects are equal. If they are not, an AssertionError without a message is thrown. If expected and actual are null, they are considered equal.

BigDecimal bd1 = new BigDecimal (1000);
BigDecimal bd2 = new BigDecimal (1000);
org.junit.Assert.assertEquals(bd1,bd2);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
8

The method assertSame tests that both are the same object. However, you have two objects which have the same value. To test this, you can use assertEquals.

However, you should be aware of some unexpected behavior when using assertEquals (which depends on the equals method) on BigDecimals. For example, new BigDecimal("100").divide(new BigDecimal("10.0")).equals(new BigDecimal("10")) evaluates to false because equals also looks at the scale of the BigDecimal instances.

In many circumstances it is better to compare BigDecimals by using the compareTo method:

assertTrue(bd1.compareTo(bd2) == 0);
Hoopje
  • 12,677
  • 8
  • 34
  • 50
2

Other alternative for specific scale and rounded:

import static org.assertj.core.api.Assertions.assertThat;

...

BigDecimal a = new BigDecimal(100.05);
BigDecimal b = new BigDecimal(100.048);

a = a.setScale(2, BigDecimal.ROUND_HALF_EVEN);
b = b.setScale(2, BigDecimal.ROUND_HALF_EVEN);

assertThat(a).isEqualTo(b);
alditis
  • 4,633
  • 3
  • 49
  • 76
2
assertThat(BigDecimal.valueOf(10.00)).isEqualByComparingTo(BigDecimal.valueOf(10));
1

bd1 and bd2 are two different objects, and since assertSame checks the object reference using the == operator, you're getting that message, see the docs:

Asserts that two objects refer to the same object. If they are not the same, an AssertionError without a message is thrown.

You should use assertEquals instead, it checks that the two objects are equal - which is what you want.


Note that comparing two BigDecimal objects using the == operator will work as long as their values are cached (for 0 through 10) values.

Maroun
  • 94,125
  • 30
  • 188
  • 241
0

If you like the assertEquals because it shows you the values in the failure message, and you want to ignore extraneous trailing decimal zeros, you can use the following:

BigDecimal bdGood = BigDecimal.valueOf(2.5).setScale(3);
BigDecimal bdBad = BigDecimal.valueOf(2.504);
assertEquals("Passes", BigDecimal.valueOf(2.5), bdGood.stripTrailingZeros());
assertEquals("Fails", BigDecimal.valueOf(2.5), bdBad.stripTrailingZeros());

This will reduce the result to the lowest possible scale before the comparison.

Todd
  • 41
  • 6
-1

Use AssertEquals instead of AssertSame... reason because assertequals checks the value but assertsame checks the refrence..

Vikrant Kashyap
  • 6,398
  • 3
  • 32
  • 52