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