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) ?

97voto

Thomas Points 686

Il y a beaucoup de réponses correctes ici, mais je voudrais ajouter ma version aussi. Celle-ci est basée sur Assertj.

import static org.assertj.core.api.Assertions.assertThat;

public class TestClass {

    public void test() {
        // do the actual test
        assertThat(actualObject)
            .isEqualToComparingFieldByFieldRecursively(expectedObject);
    }
}

MISE À JOUR : Dans assertj v3.13.2, cette méthode est dépréciée comme l'a signalé Woodz dans un commentaire. La recommandation actuelle est

public class TestClass {

    public void test() {
        // do the actual test
        assertThat(actualObject)
            .usingRecursiveComparison()
            .isEqualTo(expectedObject);
    }

}

83voto

felvhage Points 21

Mockito offre un cache-réflexion :

Pour la dernière version de Mockito, utilisez :

Assert.assertTrue(new ReflectionEquals(expected, excludeFields).matches(actual));

Pour les anciennes versions, utilisez :

Assert.assertThat(actual, new ReflectionEquals(expected, excludeFields));

52voto

Abhijeet Kushe Points 357

J'implémente généralement ce cas d'utilisation en utilisant org.apache.commons.lang3.builder.EqualsBuilder.

Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));

14voto

lopezvit Points 21

Je sais que c'est un peu vieux, mais j'espère que ça aidera.

J'ai rencontré le même problème que vous, donc, après enquête, j'ai trouvé quelques questions similaires à celle-ci, et, après avoir trouvé la solution, je réponds la même chose dans ceux, puisque je pensais qu'il pourrait aider les autres.

La réponse la plus votée (pas celui choisi par l'auteur) de cette question similaire est la solution la plus appropriée pour vous.

En gros, il s'agit d'utiliser la bibliothèque appelée Unitils .

C'est l'utilisation :

User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

qui passera même si la classe User ne met pas en œuvre equals() . Vous pouvez voir plus d'exemples et un assert vraiment cool appelé assertLenientEquals dans leur tutoriel .

12voto

Marquee Points 605

Si vous utilisez hamcrest pour vos assertions (assertThat) et que vous ne voulez pas utiliser des librairies de test supplémentaires, vous pouvez alors utiliser SamePropertyValuesAs.samePropertyValuesAs pour affirmer les éléments qui n'ont pas de méthode equals surchargée.

L'avantage est que vous n'avez pas besoin d'utiliser un autre cadre de test et qu'il donnera une erreur utile lorsque l'assertion échoue ( expected: field=<value> but was field=<something else> ) au lieu de expected: true but was false si vous utilisez quelque chose comme EqualsBuilder.reflectionEquals() .

L'inconvénient est qu'il s'agit d'une comparaison superficielle et qu'il n'y a pas d'option pour exclure des champs (comme c'est le cas dans EqualsBuilder). Vous devrez donc travailler avec des objets imbriqués (par exemple, les supprimer et les comparer indépendamment).

Meilleur cas :

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
assertThat(actual, is(samePropertyValuesAs(expected)));

Affreux cas :

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
SomeClass expected = buildExpected(); 
SomeClass actual = sut.doSomething();

assertThat(actual.getSubObject(), is(samePropertyValuesAs(expected.getSubObject())));    
expected.setSubObject(null);
actual.setSubObject(null);

assertThat(actual, is(samePropertyValuesAs(expected)));

Alors, choisissez votre poison. Cadre supplémentaire (par exemple, Unitils), erreur inutile (par exemple, EqualsBuilder), ou comparaison superficielle (hamcrest).

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