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.