152 votes

Comment affirmer l'égalité de deux classes sans méthode equals ?

Disons que j'ai une classe sans méthode equals(), dont je n'ai pas la source. Je veux affirmer l'égalité sur deux instances de cette classe.

Je peux faire des assertions multiples :

assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...

Je n'aime pas cette solution car je n'obtiens pas l'image complète de l'égalité si une assert précoce échoue.

Je peux comparer manuellement par moi-même et suivre le résultat :

String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
    errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
    errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);

Cela me donne une image complète de l'égalité, mais c'est lourd (et je n'ai même pas pris en compte les éventuels problèmes de nullité). Une troisième option consiste à utiliser un comparateur, mais compareTo() ne me dira pas quels champs n'ont pas respecté l'égalité.

Existe-t-il une meilleure pratique pour obtenir ce que je veux de l'objet, sans sous-classer et écraser les égalités (ugh) ?

9voto

Matthew Farwell Points 31257

Vous pouvez utiliser Apache commons lang ReflectionToStringBuilder

Vous pouvez spécifier les attributs que vous voulez tester un par un, ou mieux, exclure ceux que vous ne voulez pas :

String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE)
                .setExcludeFieldNames(new String[] { "foo", "bar" }).toString()

Vous comparez ensuite les deux chaînes de caractères comme d'habitude. En ce qui concerne la lenteur de la réflexion, je suppose que cela ne sert qu'à des fins de test et ne devrait donc pas être si important.

5voto

patelb Points 1574

Comme cette question est ancienne, je vais proposer une autre approche moderne utilisant JUnit 5.

Je n'aime pas cette solution car je n'obtiens pas l'image complète de l'égalité si une assert précoce échoue.

Avec JUnit 5, il y a une méthode appelée Assertions.assertAll() qui vous permettra de regrouper toutes les assertions de votre test et d'exécuter chacune d'entre elles et d'afficher les assertions qui ont échoué à la fin. Cela signifie que les assertions qui échouent en premier n'arrêteront pas l'exécution des assertions suivantes.

assertAll("Test obj1 with obj2 equality",
    () -> assertEquals(obj1.getFieldA(), obj2.getFieldA()),
    () -> assertEquals(obj1.getFieldB(), obj2.getFieldB()),
    () -> assertEquals(obj1.getFieldC(), obj2.getFieldC()));

4voto

Stefan Birkner Points 3080

La bibliothèque Hamcrest 1.3 Matchers utilitaires possède un sélecteur spécial qui utilise la réflexion au lieu des égalités.

assertThat(obj1, reflectEquals(obj2));

3voto

Avraham Shalev Points 112

Certaines des méthodes de comparaison des réflexions sont peu profondes

Une autre option consiste à convertir l'objet en un json et à comparer les chaînes de caractères.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;    
public static String getJsonString(Object obj) {
 try {
    ObjectMapper objectMapper = new ObjectMapper();
    return bjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
     } catch (JsonProcessingException e) {
        LOGGER.error("Error parsing log entry", e);
        return null;
    }
}
...
assertEquals(getJsonString(MyexpectedObject), getJsonString(MyActualObject))

2voto

Utilisation de Shazamcrest que vous pouvez faire :

assertThat(obj1, sameBeanAs(obj2));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X