JUnit使用BigDecimal进行断言

79

我想在两个小数之间使用assert,我使用以下代码:

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

但是JUnit日志显示:

expected <1000> was not: <1000>

2
它们不是预期的同一对象。您可能想要检查它们是否相等。注意:BigDecimal 不认为 1000.01000.00 相等,因为小数位数不同。在我看来,double 更简单,而且错误更少。;) - Peter Lawrey
@PeterLawrey 我想知道这个问题什么时候会被解决,我真的无法想象在任何场景下这有什么用处。 - Maroun
1
@MarounMaroun 为了向后兼容性,它永远不会被修复。 - Peter Lawrey
@kAnGeL,你能否重新查看答案,并选择最正确和最合适的答案吗? - frhack
11个回答

138

官方JUnit解决方案用于断言两个BigDecimal数值是否相等是使用Hamcrest库。

使用java-hamcrest 2.0.0.0版本,我们可以使用以下语法:

    // 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 快速参考


6
与被接受和得票最高的答案相比,这也提供了有意义的错误信息:"java.lang.AssertionError: 期望值为 <0.22>,但实际值为 <0.222>,大于期望值 <0.22>"。 - zbstof
1
不知道为什么这对我不起作用。某种类型转换/泛型的奇怪问题。java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V - Stewart

48

assertSame检查两个对象是否为同一实例。 assertEquals检查数字在值和比例上是否相等,这意味着即使是1000不等于1000.00。如果您只想比较数值,则应使用BigDecimalcompareTo()方法。

例如:

BigDecimal bd1 = new BigDecimal (1000.00);
BigDecimal bd2 = new BigDecimal (1000);
org.junit.Assert.assertTrue(bd1.compareTo(bd2) == 0); 

5
comparesEqualTo()比这更好,因为在失败时,assertThat(x.compareTo(y), is(y))会返回类似于Expected: is <0> but: was <1>的信息。而assertThat(x, comparesEqualTo(y))则会返回Expected: a value equal to <27700> but: <6700.000> was less than <27700> - slim
3
是的,使用 Hamcrest 的答案可以给出更有意义的错误信息。另一方面,我的答案不依赖于 Hamcrest,并解释了为什么被接受的答案会失败的逻辑。 - Endery

18

这个问题的答案已经给出。但有些回答回答了另一个问题,即“如何比较2个BigDecimals?” 迄今为止给出的解决方案要么是错误的,要么已过时。我建议尝试这个:

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

BigDecimal a = new BigDecimal("100")
BigDecimal b = new BigDecimal("100.00")
assertThat(a).isEqualByComparingTo(b);

@roma2341,您能否解释一下哪里出了问题?或者提供一个代码示例? - Sweder Schellens
1
方法isEqualByComparingTo()使用BigDecimal.compareTo(),这是推荐使用的。它与assertTrue(bd1.compareTo(bd2) == 0)接近,但在assertThat(a).isEqualByComparingTo(b)中,您可以看到预期值而不仅仅是错误位置。 - Mike Menko
2
这是正确的答案,谢谢。在2021年,我们不应该像穴居人一样直接使用compareTo==0。 - Steve

16

BigDecimalcompareTo() 进行比较是可行的(它忽略了小数位并比较实际数字),但在单元测试中,了解实际数字是非常有用的,特别是当测试失败时。

我在这种情况下使用的一个选项是对两个 BigDecimal 都使用 stripTrailingZeros()

assertEquals(new BigDecimal("150").stripTrailingZeros(),
                    otherBigDecimal.stripTrailingZeros());
这个函数的作用是去掉数字中的零,但不改变数字本身,因此"150"被转换为"1.5E+2"。这样做的好处是无论你在otherBigDecimal中有150150.00或其他形式,它们都会被规范化为相同的形式。
唯一的区别是,在otherBigDecimal中出现null将导致一个NullPointerException,而不是断言错误。

9

assertSame测试两个对象是否是同一个对象,即它们是否相等(==):

断言两个对象是否引用同一个对象。如果不是,则抛出没有消息的AssertionError

在你的情况下,因为bd1bd2都是新的BigDecimal对象,所以它们不是同一个对象,因此会出现异常。

你需要使用assertEquals,它测试两个对象是否相等,即.equals

断言两个对象是否相等。如果不是,则抛出没有消息的AssertionError。如果期望值和实际值都是null,则被认为是相等的。

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

1
非常感谢!你找到了我的完美解决方案:D - kAnGeL
51
只有具有完全相同标度的BigDecimal才能使用该方法。 - Pavlo Zvarych
@PavloZvarych 是的,这就是情况。这取决于您是否还想测试比例尺作为参数。 - Tunaki

8
方法assertSame测试两个对象是否相同。但是,你有两个具有相同值的对象。为了测试这一点,你可以使用assertEquals
然而,在使用assertEquals(取决于equals方法)比较BigDecimal时,你应该注意一些意外的行为。例如,new BigDecimal("100").divide(new BigDecimal("10.0")).equals(new BigDecimal("10"))的结果为false,因为equals还要考虑BigDecimal实例的标度。
在许多情况下,最好使用compareTo方法来比较BigDecimal
assertTrue(bd1.compareTo(bd2) == 0);

3

特定比例和圆角的其他替代方案:

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

2
assertThat(BigDecimal.valueOf(10.00)).isEqualByComparingTo(BigDecimal.valueOf(10));

1
bd1bd2 是两个不同的对象,由于 assertSame 使用 == 操作符检查对象引用,因此你会得到该消息,请参阅文档:

断言两个对象引用同一个对象。如果它们不相同,则抛出没有消息的 AssertionError

你应该使用 assertEquals,它检查两个对象是否相等 - 这正是你想要的。


请注意,使用==运算符比较两个BigDecimal对象只有在它们的值被缓存(0到10)时才会起作用。

0
如果您喜欢使用assertEquals,因为它可以在失败消息中显示值,并且您想忽略多余的尾部小数零,则可以使用以下代码:
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());

在比较之前,这将把结果缩小到最低可能的范围。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接