assert равно int long float

Есть ли элегантный способ утверждать равные числа, игнорируя их классы? Я хочу использовать его в рамках тестов JUnit, но, например,

Assert.assertEquals(1,1L)

fail с java.lang.AssertionError: ожидается: java.lang.Integer <1> но был: java.lang.Long <1>

Я ожидаю, что есть хороший метод где-то, который сравнивает только значение и работает с int, long, float, byte, double, BigDecimal, BigInteger, вы называете это...

Ответ 1

Одним из обходных путей с некоторыми накладными расходами было бы обернуть значения в объектах BigDecimal, поскольку перегрузки конструктора BigDecimal занимают long, int и double примитивы.

Так как new BigDecimal(1l).equals(new BigDecimal(1.0)) имеет место true,

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

должен работать на вас.

редактировать

Как Hulk говорится ниже, масштаб BigDecimal объектов используется в equals сравнения, но не в compareTo сравнения. В то время как для шкалы установлено значение по умолчанию 0 для long конструктора, оно выводится через некоторый расчет в double конструкторе. Поэтому самый безопасный способ сравнения значений (т.е. В крайних случаях для double значений) может быть compareTo методом сравнения, а проверка результата - 0.

Ответ 2

Оберните эту функциональность в свой собственный 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));

Таким образом, вы можете повторно использовать ваш помощник, и утверждение assert будет более читаемым в конце.

Отказ от ответственности: это только псевдокод и еще не проверен, но должен работать с некоторой настройкой.

Но остерегайтесь также из сравнения чисел в Java

Ответ 3

Согласно моему чтению JLS, разрешение перегрузки для

Assert.assertEquals(1,1L)

должны решить

Assert.assertEquals(long, long)

(Для записи assertEquals(long, long), assertEquals(float, float) и assertEquals(double, double) применяются строгим вызовом, а первый - наиболее конкретным, см. JLS 15.12.2.2. Строгий контекст вызова позволяет примитивное расширение, но не бокс или unboxing.)

Если (как показывают данные), ваш вызов разрешает Assert.assertEquals(Object, Object), что подразумевает, что один из операндов уже должен быть коробочным. Проблема с этой перегрузкой заключается в том, что она использует метод equals(Object) для сравнения объектов, а контракт для этого метода указывает, что результат является false если соответствующие типы объектов различны.

Если это то, что происходит в вашем реальном коде, я сомневаюсь, что предложение использовать is(T) Matcher будет работать. is(T) согласовань эквивалентно is(equalTo(T)), а второй опирается на equals(Object)...

Есть ли существующий "хороший метод"?

AFAIK, no.

Я думаю, что реальное решение должно быть немного более внимательным к типам; например

 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 тоже сработает.

Ответ 4

Создайте свои собственные методы assert и сравните двойные значения для примитивов. Если используется 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

Кроме того, в случае, если значение не является целым числом (int, Integer, long, Long), но с плавающей точкой (float, double, Float, Double), метод должен также принимать аргумент epsilon чтобы терпеть неточность из-за представления.

Итак, вот идея реализации (на данный момент я игнорирую случаи NaN и положительные и отрицательные нули двойки - они могут быть добавлены, если требуется действительно прочная реализация)

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