【问题标题】:assert equals int long float断言等于 int long float
【发布时间】:2016-12-20 12:14:21
【问题描述】:

有没有一种优雅的方式来断言数字相等而忽略它们的类?我想在 JUnit 测试框架中使用它,但是例如

Assert.assertEquals(1,1L)

因 java.lang.AssertionError 失败:预期:java.lang.Integer 但为:java.lang.Long

我希望某处有一个很好的方法,它只比较值并适用于 int、long、float、byte、double、BigDecimal、BigInteger,你可以命名它......

【问题讨论】:

  • 为什么这被否决了?对我来说似乎是一个合法的问题。
  • 我同意。投赞成票!我也认为答案并非微不足道。
  • 我在想Number.toString.equals,但是有科学的格式可以处理
  • 对于原语,您可以创建重载assertEquals(int, int)assertEquals(long, long)assertEquals(float, float) 等,它们都调用Assert.assertEquals(a, b),重载解析将选择合适的。在某些情况下,您很可能会遇到模棱两可的过载问题。我认为针对原语为Big* 执行此操作可能会变得更加棘手。我会问你为什么真的想要这个 - 不同类型的操作数有多频繁?
  • 如果我运行那段代码,它就可以正常工作...(Java 8,Junit 4.11)也许Assert.assertThat(1, is(1L)); 也可能对您有所帮助...您可能需要实现自己的@987654328 @然后...

标签: java junit


【解决方案1】:

一种需要一些开销的解决方法是将值包装在BigDecimal 对象中,因为BigDecimal 构造函数重载采用longintdouble 原语。

由于new BigDecimal(1l).equals(new BigDecimal(1.0)) 持有true

Assert.assertEquals(new BigDecimal(1.0), new BigDecimal(1l));  

应该适合你。

编辑

正如下面Hulk 所述,BigDecimal 对象的比例用于equals 比较,但不用于compareTo 比较。 虽然使用long 的构造函数的比例设置为默认0,但它是通过使用double 的构造函数中的一些计算推断出来的。 因此,比较值的最安全方法(即在 double 值的边缘情况下)可能是通过调用 compareTo 并检查结果是 0

【讨论】:

  • 我接受这个,因为它是优雅的单线解决方案
  • @PavelNiedoba 欢呼。不过要注意开销,您每次都在创建对象。
  • 使用equals比较BigDecimals时必须注意:它与compareTo不一致,因为它还考虑了规模,JavaDocs of BigDecimal.equals另见stackoverflow.com/q/6787142
  • @Hulk 好点。也许比较BigDecimals 中的doubleValue 可能更不容易出错?我看到比例是计算出来的,至少在BigDecimal(double) 构造函数中,所以我想知道在上面初始化时它是否有时确实会有所不同......
  • @Hulk 或者以其他方式使用 compareTo 我猜,没必要让事情变得更难:)
【解决方案2】:

根据我对 JLS 的阅读,对于

Assert.assertEquals(1,1L)

应该解决

Assert.assertEquals(long, long)

简而言之,问题中的代码 sn-p 不是您实际问题的有效示例。

(记录一下,assertEquals(long, long)assertEquals(float, float)assertEquals(double, double)适用于严格调用,第一个是最具体的;参见JLS 15.12.2.2。严格调用上下文允许原始加宽,但不能装箱或拆箱。)

如果(如证据所示)您的调用解析为Assert.assertEquals(Object, Object),这意味着其中一个操作数必须已经是盒装类型。该重载的问题在于它使用equals(Object) 方法来比较对象,并且该方法的约定指定如果对象各自的类型不同,则结果为false

如果您的真实代码中发生了这种情况,那么我怀疑使用 is(T) Matcher 的建议是否会起作用。 is(T) 匹配器等价于is(equalTo(T)),后者依赖于equals(Object) ...

有没有现成的“好方法”?

AFAIK,不。

我认为真正的解决方案是更加关注类型;例如

 int i = 1;
 Long l = 1L;
 Assert.assertEquals(i, l);         // Fails
 Assert.assertEquals((long) i, l);  // OK - assertEquals(Object, Object)
 Assert.assertEquals((Long) i, l);  // OK - assertEquals(Object, Object)
 Assert.assertEquals(i, (int) l);   // OK - assertEquals(long, long) 
                                    //      it would bind to an (int, int) 
                                    //      overload ... it it existed.   
 Assert.assertEquals(i, (long) l);  // OK - assertEquals(long, long)


 

编写自定义 Matcher 也可以。

【讨论】:

    【解决方案3】:

    将该功能包装在您自己的 Matcher 中,并将其与 assertThat 一起使用。

    样本匹配器:

    class IsAnyNumber extends BaseMatcher {
      final Object expected;
      //...
      public boolean matches(Object actual) {
        // compare / transform / check type / ensure: String, double, int, long
        // example via BigDecimal as seen from Mena (without checks)
        return new BigDecimal(expected).equals(new BigDecimal(actual));
      }
      // ...
    }
    
    // somewhere else:
    public static IsAnyNumber is(Object expected) {
      return new IsAnyNumber(expected);
    }
    

    然后在您的测试中调用该静态方法:

    assertThat(1, is(1L));
    assertThat(1, is(1.0));
    assertThat(1L, is(1));
    

    这样你可以重复使用你的匹配器,并且断言语句最终更具可读性。

    免责声明:这只是伪代码,尚未经过测试,但应该可以进行一些调整。

    但也要注意Comparing Numbers in Java

    【讨论】:

      【解决方案4】:

      创建您自己的断言方法并比较原语的双精度值。如果使用BigDecimal,则必须将原始值转换为BigDecimal

      static void assertEquals(Number number1, Number number2) {
        Assert.assertEquals(number1.doubleValue(), number2.doubleValue());
      }
      
      static void assertEquals(BigDecimal number1, BigDecimal number2) {
        if (number2.compareTo(number1) != 0) {
          Assert.fail("Values are not equal. ..... ");
        }
      }
      
      static void assertEquals(Number number1, BigDecimal number2) {
        assertEquals(new BigDecimal(number1.doubleValue()), number2);
      }
      
      static void assertEquals(BigDecimal number1, Number number2) {
        assertEquals(number2, number1);
      }
      

      可以这样使用:

      assertEquals(1, new BigDecimal("1.0"));
      assertEquals(1.0d, 1);
      assertEquals(new Float(1.0f), 1.0d);
      assertEquals(new BigDecimal("1.00000"), new BigDecimal("1.0"));
      ...
      

      【讨论】:

        【解决方案5】:

        我认为要接受所有八种数值类型(原始和对象),该方法必须采用字符串参数。调用者必须记住通过这个习语将值转换为字符串:

        ""+value
        

        此外,如果值不是整数(intIntegerlongLong)而是浮点表示(floatdoubleFloat、@987654329 @),该方法还必须带一个参数 epsilon 以容忍由于表示而导致的不精确性。

        所以这是一个实现想法(现在我忽略 NaN 和 double 的正负零的情况——如果需要真正可靠的实现,可以添加这些)

        private static boolean equalsNumerically(String n1String
                                                , String n2String
                                                , double epsilon) {
            try {
                Long n1Long = new Long(n1String);
                Long n2Long = new Long(n2String);
                return n1Long.equals(n2Long);
            } catch (NumberFormatException e) {
                /*
                 * If either one of the number is not an integer, try comparing
                 * the two as Double
                 */
                try {
                    Double n1Double = new Double(n1String);
                    Double n2Double = new Double(n2String);
                    double delta = ( n1Double - n2Double) / n2Double;
                    if (delta<epsilon) {
                        return true;
                    } else {
                        return false;
                    }
                } catch (NumberFormatException e2) {
                    return false;
                }
            } 
        }
        

        测试代码

            int     primitiveInt = 1;
            long    primitiveLong = 1L;
            float   primitiveFloat = 0.999999F;
            double  primitiveDouble = 0.999999D;
            Integer objectInt = new Integer(1);
            Long    objectLong = new Long(1);
            Float   objectFloat = new Float(0.999999);
            Double  objectDouble = new Double(0.999999);
        
            final double epsilon = 1E-3;
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveLong, 0)); 
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+primitiveLong, 0): %s %s %s%n"
                    , primitiveInt, primitiveLong, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveLong, epsilon));
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+primitiveLong, epsilon)): %s %s %s%n"
                    , primitiveInt, primitiveLong, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveFloat, epsilon)); 
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+primitiveFloat, 0): %s %s %s%n"
                    , primitiveInt, primitiveFloat, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveDouble, epsilon)); 
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+primitiveDouble, epsilon): %s %s %s%n"
                    , primitiveInt, primitiveDouble, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectInt, 0)); 
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+objectInt, 0): %s %s %s%n"
                    , primitiveInt, objectInt, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectLong, 0)); 
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+objectLong"
                    + ", \"\"+objectLong, 0): %s %s %s%n"
                    , primitiveInt, primitiveLong, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectFloat, epsilon));
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+objectFloat, epsilon)): %s %s %s%n"
                    , primitiveInt, objectFloat, epsilon);
        
            Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectDouble, epsilon)); 
            System.out.format("Test passed: "
                    + "Assert.assertTrue(equalsNumerically(\"\"+primitiveInt"
                    + ", \"\"+objectDouble, 0): %s %s %s%n"
                    , primitiveInt, objectDouble, epsilon);
        

        测试输出

        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveLong, 0): 1 1 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveLong, epsilon)): 1 1 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveFloat, 0): 1 0.999999 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+primitiveDouble, epsilon): 1 0.999999 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectInt, 0): 1 1 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectLong, 0): 1 1 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectFloat, epsilon)): 1 0.999999 0.001
        Test passed: Assert.assertTrue(equalsNumerically(""+primitiveInt, ""+objectDouble, 0): 1 0.999999 0.001
        

        【讨论】:

        • 最好使用String.valueOf(value) 然后""+value。因为它更干净,更容易理解。
        猜你喜欢
        • 2018-04-19
        • 2013-09-25
        • 2017-05-25
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        • 1970-01-01
        • 2019-01-29
        相关资源
        最近更新 更多