Certains assertEquals sont plus égaux que d’autres

Une barre rouge inattendue. Je scrute le message d’échec du test dans le panneau junit :

Je double-clique sur expected: pour obtenir le comparateur de l’IDE. Zut rien ne se passe ! Du coup je scrolle pour voir tout le message d’erreur, mais je ne distingue aucune différence entre résultats attendu et obtenu. Et là je m’interroge : pourquoi je n’obtiens pas le comparateur quand je double-clique :-( ?

Ca ne vous est jamais arrivé ? Dans mon équipe, ça arrive tout le temps.

Eclipse et intelliJ IDEA réagissent de la même manière. Leur comportement dépend de l’exception qu’il reçoivent :

  • avec AssertionError le double-clic ne fait rien
  • avec ComparisonFailure le double-clic ouvre la belle popup du comparateur :

Quand ces exceptions sont-elles levées ?

  • ComparisonFailure est levée par assertEquals(String, String)
  • AssertionError est levée par assertEquals(Object, Object) et assertThat

AssertionError n’est pas spécifique à junit, c’est une java.lang.AssertionError, elle peut provenir du code lui-même plutôt que du test. L’IDE ne l’interprète pas comme le résultat d’un test.

De loin, les messages semblent identiques [1] car assertEquals(Object, Object) affiche des toString(). Voilà pourquoi les variations de comportement du double-clic surprennent. Certains en viennent à ajouter des toString() pour souligner les différences :

assertEquals(attendu.toString(), obtenu.toString());

C’est dangereux car l’égalité n’est pas déterminée par le equals() de Object, ce qui viole un contrat Java. En plus, ça ne marche pas pour assertThat qui lève AssertionError même pour deux String (assertThat("Philippe", equalTo("Blayo")). J’aimerais plutôt que :

  • equals() détermine la réussite ou l’échec du test (rouge/vert)
  • une comparaison entre les toString() enrichisse l’affichage expected/actual des barres rouges, quelque soit la classe des objets comparés

Suivant le contexte, je connais deux manières d’y parvenir :

  • utiliser une lib qui lève déjà une ComparisonFailure comme FEST-assert
  • coder soi-même une assertion qui lève une ComparisonFailure (custom assertion). En laissant de coté la gestion des null, la substance d’une telle assertion serait :
    static void assertLisible(Object expected, Object actual) {
        if (! expected.equals(actual))
            throw new ComparisonFailure("",
                expected.toString(), actual.toString());
    }

[1] ComparisonFailure met entre crochets la différence bl[ay]o / bl[ya]o, là où AssertionError laisse blayo / blyao. Les messages ne sont donc identiques qu’en apparence. Mais ces crochets ne suffisent pas : une popup ne s’ouvre qu’avec une ComparisonFailure.

Cette entrée a été publiée dans java, tests, avec comme mot(s)-clef(s) , , , . Vous pouvez la mettre en favoris avec ce permalien.

2 réponses à Certains assertEquals sont plus égaux que d’autres

  1. dibus dit :

    J’ai régulièrement rencontré ce genre de soucis, notamment pour la comparaison de String.
    De notre côté, nous avons tendance à remplacer
    assertEquals(attendu.toString(), obtenu.toString());</code
    par
    assertTrue(attendu.equals(obtenu));

    Ta technique avec la création de la fonction assertLisible me paraît beaucoup plus élégante : On en parle rapidement avec l'équipe ! Merci :)

  2. Laurent L. dit :

    Bonjour,

    c’est marrant de retrouver dibus içi ;) finalement on a encore les mêmes questionnements sur le testing !

    Bref, chez nous, on est partit sur l’option FestAssert, avec l’idée de garder assertEqual(ObjetAttendu,ObjetObtenu) quitte à implémenter un comparateur.

    Principale justification: c’est ce qui m’a été spécifié par mon Product Owner dans les critères d’acceptation de la story que teste.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>