web-dev-qa-db-fra.com

affirmer est égal à long float

Existe-t-il un moyen élégant d'affirmer que les nombres sont égaux tout en ignorant leurs classes? Je veux l'utiliser dans le framework de tests JUnit mais par exemple

Assert.assertEquals(1,1L)

échoue avec Java.lang.AssertionError: attendu: Java.lang.Integer <1> mais était: Java.lang.Long <1>

Je pense qu’il existe une méthode Nice quelque part qui compare uniquement la valeur et fonctionne avec int, long, float, octet, double, BigDecimal, BigInteger, nommez-le.

16
Pavel Niedoba

Une solution de contournement consiste à insérer les valeurs dans BigDecimal objects, car les surcharges de constructeur BigDecimal acceptent les primitives long, int et double

Depuis new BigDecimal(1l).equals(new BigDecimal(1.0)) vaut true

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

devrait travailler pour vous.

Modifier

Comme Hulk indique ci-dessous, l'échelle des objets BigDecimal est utilisée dans la comparaison equals, mais pas dans la comparaison compareTo. Bien que l'échelle soit définie sur un 0 par défaut pour le constructeur prenant long, elle est déduite par un calcul dans le constructeur prenant double. Par conséquent, le moyen le plus sûr de comparer les valeurs (c'est-à-dire dans les cas Edge pour les valeurs double) peut-être est d'appeler compareTo et de vérifier le résultat, à la place, 0.

11
Mena

D'après mes lectures sur le JLS, la résolution de surcharge pour

Assert.assertEquals(1,1L)

devrait résoudre à

Assert.assertEquals(long, long)

(Pour mémoire, assertEquals(long, long), assertEquals(float, float) et assertEquals(double, double) sont applicables par invocation stricte, et le premier est le plus spécifique; voir JLS 15.12.2.2 . Le contexte d'invocation strict autorise un élargissement primitif, mais pas la mise en boîte ou unboxing.)

Si (comme le suggère la preuve) votre appel résout Assert.assertEquals(Object, Object), cela implique que l'un des opérandes doit déjà être un type encadré. Le problème avec cette surcharge est qu’il utilise la méthode equals(Object) pour comparer des objets et que le contrat pour cette méthode spécifie que le résultat est false si les types respectifs des objets sont différents.

Si c'est ce qui se passe dans votre code réel, alors je doute que la suggestion d'utiliser la fonction is(T)Matcher fonctionne également. L'attribut is(T) équivaut à is(equalTo(T)) et ce dernier s'appuie sur equals(Object) ...

Existe-t-il une "méthode de Nice" existante? 

Autant que je sache, non.

Je pense que la vraie solution est d’être un peu plus attentif aux types; par exemple. 

 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)

Ecrire une coutume Matcher fonctionnerait aussi.

5
Stephen C

Emballez cette fonctionnalité dans votre propre Matcher et utilisez-la avec assertThat.

Échantillon de matcher:

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);
}

Dans vos tests, vous appelez ensuite cette méthode statique:

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

De cette façon, vous pouvez réutiliser votre outil de correspondance et la déclaration d'assertion est plus lisible à la fin.

Avertissement: il ne s'agit que de pseudo-code et n'a pas encore été testé, mais devrait fonctionner avec quelques ajustements.

Mais méfiez-vous aussi de Comparaison des nombres en Java

4
Roland

Créez vos propres méthodes d'assertion et comparez les valeurs doubles des primitives. Si une BigDecimal est utilisée, la valeur de la primitive doit être convertie en une 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);
}

Il peut être utilisé de cette façon: 

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"));
...
1
Stefan

Je pense que pour accepter les huit types de valeurs numériques (primitive et objet), la méthode doit prendre des arguments de type chaîne. L'appelant devra se rappeler de transtyper la valeur en chaîne par cet idiome:

""+value

De même, si la valeur n'est pas un entier (int, Integer, long, Long), mais une représentation en virgule flottante (float, double, Float, Double), la méthode doit également prendre un argument epsilon pour tolérer l'imprécision due à la représentation.

Voici donc une idée de mise en œuvre (pour l'instant, j'ignore les cas de NaN et les zéros positif et négatif de double - ils peuvent être ajoutés si une mise en œuvre réellement solide est nécessaire).

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;
        }
    } 
}

Code de test

    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 de sortie

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
0
leeyuiwah