【问题标题】:Junit difference between assertEquals(Double, Double) and assertEquals(double, double, delta)assertEquals(Double, Double) 和 assertEquals(double, double, delta) 之间的 Junit 区别
【发布时间】:2012-06-28 10:02:26
【问题描述】:

我有一个 junit 测试断言两个 Double 对象具有以下内容:

Assert.assertEquals(Double expected, Double result);

这很好,然后我决定将其更改为使用原始双精度而不是原来的双精度,除非您还提供增量,否则它已被弃用。

所以我想知道的是在这个 assertEquals 中使用 Double 对象或原始类型有什么区别?为什么使用没有 delta 的对象可以,但不推荐使用没有 delta 的原语? Java 是否在后台执行某些已考虑默认增量值的操作?

谢谢。

【问题讨论】:

    标签: java unit-testing junit


    【解决方案1】:

    没有签名的assert method in JUnit

    assertEquals(Double expected, Double result);
    

    但是,有一个通用的对象:

    assertEquals(Object expected, Object result);
    

    这会调用对象的equals 方法,正如您所料,不建议使用它来比较Double 对象。

    正如您所观察到的,对于双精度数,绝对有必要使用增量进行比较,以避免浮点舍入问题(已在其他一些答案中解释)。如果您使用带有double 参数的assertEquals 的3 参数版本

    assertEquals(double expected, double actual, double delta);
    

    您的 Doubles 将被无声地拆箱到 double 并且一切正常(并且您的测试不会意外失败:-)。

    【讨论】:

    • +1 重新拆箱,是的,但需要注意的是,如果一个或另一个 Double 为空,您的行为会略有不同。使用 Object,您会收到一条很好的消息,使用 delta,您会收到 NullPointerException。
    • 我的经验是,在某些情况下,这些方法不适用于非常大的数字,尤其是当您的数据变化超过几个数量级时。两个数字可能有非常小的百分比误差,但需要一个看似很大的增量才能匹配。但同样的增量不适用于数据中更小的值。在某些情况下,即使是非常大的数字也有望匹配几乎所有有效数字。在其他情况下,例如在比较两种优化的 FFT 算法时,这种可能性要小得多。一种更稳健的方法是约束相对误差。
    • @orodbhen 为了使它起作用,应该根据期望值的指数计算 delta,所以如果 double e 是期望值,那么 delta 应该是 double d = Math.pow(10, Math.log10(Math.abs(e)) - 12); 其中12 是最大实用有效位数。如果将 log10 乘以 e 具有较大的正指数而失败,相反,如果将 log10 乘以值 > 1.0,它将因 e 具有较大的负指数而失败 - 并且这就是你写的效果。因此,请确保 delta 非常适合期望值的指数。
    • "你的双打会默默地拆箱变成双打,一切都会正常工作";那么......如果我的预期和结果都是null 使用第二个签名会发生什么?
    【解决方案2】:

    双重数学很少给出完全相等的结果。例如,0.1 * 0.1 != 0.01。在比较双精度结果时,通常至少需要一些 delta。

    另一方面,如果您比较盒装的Doubles,它假定您想要完全相等。 Java 没有考虑默认的 delta 值,但 Double.equals== 的行为略有不同:尤其是 its handling of NaNs

    这在测试中是有道理的,因为Double.NaN != Double.NaN,但在测试中,如果您期望返回NaNNaN,那么这是一个正确的答案。

    【讨论】:

      【解决方案3】:

      最好这样写:

      assertEquals(23.0, 250.0, 0.0)  
      

      0.0 - 它是增量。了解为什么不推荐使用您的方法。

      【讨论】:

        【解决方案4】:

        SOURCE. 断言两个双精度或浮点数在正增量内相等。如果不是,则抛出 AssertionError。如果期望值为无穷大,则忽略 delta 值。NaN 被视为相等。

        【讨论】:

          【解决方案5】:

          我想说,如果没有 delta,比较双精度数、原始数或对象数是没有用的。了解流动点数的工作原理是进行数值工作的关键。

          对象可能在幕后使用 .equals;除了==,原语别无选择。

          仅仅因为对象版本没有使用增量并不能使它成为一个更好的主意。

          【讨论】:

            【解决方案6】:

            我正在回答有关 JUnit 5 的更新。无论版本如何,assertEquals(Double expected, Double actual) 都会被路由到 assertEquals(Object expected, Object actual),因为正如其他人已经解释的那样,Double 是一个隐式扩展 Object 的原始包装器。

            但是 JUnit 4 上下文中的 assertEquals(double expected, double actual)(使用两个原语而不是两个原语包装器)调用了一个已弃用的过程。我不知道它是否已经在 J​​Unit 3 中被弃用。但猜猜看:它在 JUnit 5 中没有被弃用。

            double 这样的浮点类型本质上是不精确的。假设expected = 0.3actual 是如何计算的?如果是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: 预期: 但是是: 在 org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55) 在 org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62) 在 org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:70) 在 org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:65) 在 org.junit.jupiter.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 应该在右大括号之前的行中显示删除线)。即使expectedactual 具有完全相同的位模式(在本例中不会),此测试也会失败。

            java.lang.AssertionError: 使用 assertEquals(expected, actual, delta) 比较浮点数 org.junit.Assert.fail(Assert.java:88) 在 org.junit.Assert.assertEquals(Assert.java:667) 在 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 中仍然可用。从 Scala 的角度来看,您可以将其视为 assertEquals(expected: Double, actual: Double, delta: Double = 0.0)

            【讨论】:

              猜你喜欢
              • 2016-05-06
              • 1970-01-01
              • 2012-08-26
              • 1970-01-01
              • 2017-07-07
              • 1970-01-01
              • 1970-01-01
              • 2021-11-07
              相关资源
              最近更新 更多