我有一个junit测试断言两个Double对象具有以下内容:
Assert.assertEquals(Double expected, Double result);
这很好,然后我决定将其更改为使用原始双精度而不是被弃用,除非您还提供增量。
所以我想知道在这个 assertEquals 中使用 Double 对象或原始类型有什么区别?为什么使用没有 delta 的对象可以,但不推荐使用没有 delta 的原语?Java 是否在后台做一些已经考虑到默认增量值的事情?
谢谢。
我有一个junit测试断言两个Double对象具有以下内容:
Assert.assertEquals(Double expected, Double result);
这很好,然后我决定将其更改为使用原始双精度而不是被弃用,除非您还提供增量。
所以我想知道在这个 assertEquals 中使用 Double 对象或原始类型有什么区别?为什么使用没有 delta 的对象可以,但不推荐使用没有 delta 的原语?Java 是否在后台做一些已经考虑到默认增量值的事情?
谢谢。
JUnit 中没有带有签名的断言方法
assertEquals(Double expected, Double result);
然而,有一个通用的对象:
assertEquals(Object expected, Object result);
这会调用对象的equals
方法,正如您所料,不建议使用它来比较Double
对象。
正如您所观察到的,对于双精度数,绝对有必要使用 delta 进行比较,以避免浮点舍入问题(已在其他一些答案中解释过)。如果您使用assertEquals
带double
参数的 3 参数版本
assertEquals(double expected, double actual, double delta);
您Double
的 s 将被默默地拆箱double
,一切都会正常工作(并且您的测试不会意外失败:-)。
双重数学很少给出完全相同的结果。例如,0.1 * 0.1 != 0.01
。在比较双精度结果时,通常至少需要一些增量。
另一方面,如果您要比较 boxed Double
s,它假定您想要完全相等。Java 没有考虑默认的 delta 值,但Double.equals
与 的行为略有不同==
:特别是它对 NaN 的处理。
这在测试中是有意义的,因为Double.NaN != Double.NaN
,但是在测试中,如果您期望 anNaN
并且NaN
返回,那是一个正确的答案。
最好写这样的东西:
assertEquals(23.0, 250.0, 0.0)
0.0 - 它是增量。阅读为什么不推荐使用您的方法。
资源。 断言两个双精度或浮点数在正增量内相等。如果不是,则抛出 AssertionError。如果期望值为无穷大,则忽略增量值。NaN 被视为相等。
我会说比较双打,原始或对象,没有增量是没有用的。了解流动点数的工作原理是进行数值工作的关键。
该对象可能在幕后使用 .equals ;除了==之外,原语别无选择。
仅仅因为对象版本没有使用增量并不能使它成为一个更好的主意。
我正在回答有关 JUnit 5 的更新。无论版本如何,assertEquals(Double expected, Double actual)
都会被路由到assertEquals(Object expected, Object actual)
,因为正如其他人已经解释的那样,Double
它是隐式扩展的原始包装器Object
。
但是assertEquals(double expected, double actual)
(使用两个原语而不是两个原语包装器)在 JUnit 4 上下文中调用已弃用的过程。我不知道它是否已经在 JUnit 3 中被弃用。但猜猜看:它在 JUnit 5 中没有被弃用。
像这样的浮点类型double
本质上是不精确的。假设expected = 0.3
。是如何actual
计算的?如果3.0 / 10
是结果可能是准确的。但如果它是臭名昭著的0.1 + 0.2
,它将偏离 0.00000000000000004(如果减去 0.3 ,它会以某种方式变为 5.551115123125783 × 10 -17 )。
一般来说,浮点乘法和除法比浮点加法和减法更可靠。
以下示例来自具有org.junit.jupiter.api.Assertions.*
静态导入的测试类。
@Test
void testDeltaExample() {
double expected = 0.3;
double actual = 0.1 + 0.2;
assertEquals(expected, actual);
}
结果:
org.opentest4j.AssertionFailedError: 预期: <0.3> 但在 org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55) 处为 <0.30000000000000004> 在 org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils .java:62) 在 org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:65) 在 org.junit.jupiter 的 org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:70)。 api.Assertions.assertEquals(Assertions.java:868) ...
你在乎它是否会像那样减少一点吗?如果你不这样做,那么你会想要一个像 0.0000000000000001 这样的增量。
如果需要,您仍然可以使用 JUnit 5 中的“老式”JUnit(但除了迁移现有测试套件或看到它可以完成之外,您不应该出于任何原因想要这样做)。
@Test
void testDeltaExample() {
double expected = 0.3;
double actual = 0.1 + 0.2;
org.junit.Assert.assertEquals(expected, actual);
}
(您的 IDE 应该在右大括号之前的行中显示删除线)。即使具有完全相同的位模式(在此示例中不会),expected
此测试也会失败。actual
java.lang.AssertionError: 使用 assertEquals(expected, actual, delta) 在 org.junit.Assert.assertEquals(Assert.java:667) 比较 org.junit.Assert.fail(Assert.java:88) 的浮点数) 在 org.junit.Assert.assertEquals(Assert.java:656) ...
许多非常聪明的 Java 程序员学习了 SQL 连接等复杂的东西,根本不关心浮点数,所以他们只使用 0.0 的 delta。
因此,JUnit 开发人员似乎认为教育人们了解浮点的弱点不是他们的工作。所以org.junit.jupiter.api.Assertions.assertEquals(double expected, double actual)
在技术上是新的,根本没有被弃用。
绝对清楚,在 JUnit 5 中仍然可以使用 delta 进行浮点比较。从 Scala 的角度来看,您可以将其视为assertEquals(expected: Double, actual: Double, delta: Double = 0.0)
.