web-dev-qa-db-fra.com

Pouvez-vous ajouter un message personnalisé à AssertJ assertThat?

Nous avons une suite de tests qui utilise principalement les assertions JUnit avec les égaleurs Hamcrest. Une de nos équipes a commencé à expérimenter avec AssertJ et a impressionné les gens par sa syntaxe, sa flexibilité et son caractère déclaratif. Il y a une fonctionnalité que JUnit fournit pour laquelle je ne trouve pas d'équivalent dans AssertJ: l'ajout d'un message d'échec d'assertion personnalisé.

Nous comparons souvent des objets qui ne sont pas faits pour la lisibilité humaine et qui auront des identifiants ou des UUID d'apparence aléatoire et il est impossible de dire ce qu'ils sont censés être par les données qu'ils contiennent. C'est une situation inévitable pour notre base de code, malheureusement, car une partie de l'objectif qu'il remplit est de mapper les données entre d'autres services sans nécessairement comprendre ce que c'est.

Dans JUnit, la méthode assertThat fournit une version avec un String reason paramètre avant le Matcher<T> param. Cela rend trivial d'ajouter une courte chaîne de débogage éclairant le problème, comme ce que la comparaison devrait signifier pour un humain.

AssertJ, d'autre part, fournit un milliard différent générique static assertThat méthodes qui retournent une certaine forme de interface Assert ou l'une de ses nombreuses classes d'implémentation. Cette interface ne fournit pas un moyen standard de définir un message personnalisé à inclure dans les échecs.

Existe-t-il un moyen d'obtenir cette fonctionnalité à partir de l'API AssertJ ou de l'une de ses extensions sans avoir à créer une classe d'assert personnalisée pour chaque type d'assert auquel nous voulons ajouter des messages?

63
Patrick M

Et de façon classique, j'ai trouvé ce que je cherchais quelques instants après avoir posté la question. J'espère que cela facilitera la recherche de la prochaine personne sans avoir à savoir d'abord comment elle s'appelle. La méthode magique est le nom trompeusement court as , qui fait partie d'une autre interface que AbstractAssert implémente: Descriptable , pas la base Assert l'interface.

public S as(String description, Object... args)

Définit la description de cet objet prenant en charge la syntaxe String.format(String, Object...).
Exemple :

try {
  // set a bad age to Mr Frodo which is really 33 years old.
  frodo.setAge(50);
  // you can specify a test description with as() method or describedAs(), it supports String format args
  assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);
} catch (AssertionError e) {
  assertThat(e).hasMessage("[check Frodo's age] expected:<[33]> but was:<[50]>");
}

Où cette chaîne entre guillemets dans le bloc catch hasMessage est ce qui apparaît dans votre journal de sortie de test unitaire si l'assertion échoue.


J'ai trouvé cela en remarquant l'assistant failWithMessage dans la page d'assertion personnalisée liée dans la question. JavaDoc pour cette méthode indique qu'elle est protégée, elle ne peut donc pas être utilisée par les appelants pour définir un message personnalisé. Il mentionne cependant l'assistant as:

De plus, cette méthode respecte toute description définie avec as(String, Object...) ou tout message d'erreur remplacé défini par l'utilisateur avec overridingErrorMessage(String, Object...).

... et l'aide overridingErrorMessage , qui remplace complètement le message AssertJ expected: ... but was:... standard par la nouvelle chaîne fournie.

La page d'accueil AssertJ ne mentionne aucun de ces assistants jusqu'à ce que la page surligne les fonctionnalités, qui montre des exemples de l'assistant as dans la section Soft Assertions , mais ne décrit pas directement ce qu'il fait.

109
Patrick M

Pour ajouter une autre option à la réponse de Patrick M:

Au lieu d'utiliser Descriptable.as, Vous pouvez également utiliser AbstractAssert.withFailMessage() :

try {
  // set a bad age to Mr Frodo which is really 33 years old.
  frodo.setAge(50);
  // you can specify a test description via withFailMessage(), supports String format args
  assertThat(frodo.getAge()).
    withFailMessage("Frodo's age is wrong: %s years, difference %s years",
      frodo.getAge(), frodo.getAge()-33).
    isEqualTo(33);
} catch (AssertionError e) {
  assertThat(e).hasMessage("Frodo's age is wrong: 50 years, difference 17 years");
}

La différence avec l'utilisation de Descriptable.as Est qu'elle vous donne un contrôle complet sur le message personnalisé - il n'y a pas de "prévu" et "mais était ".

Ceci est utile lorsque les valeurs réelles testées ne sont pas utiles pour la présentation - cette méthode vous permet d'afficher à la place d'autres valeurs, éventuellement calculées, ou aucune.


Notez que, tout comme Descriptable.as, Vous devez appeler withFailMessage() avant toute assertion réelle - sinon cela ne fonctionnera pas, car l'assertion se déclenchera en premier. Ceci est noté dans le Javadoc.

7
sleske

Pour info, cela est documenté dans le nouveau site Web AssertJ (qui est toujours en construction mais qui contient déjà des informations utiles), voir https://assertj.github.io/doc/#assertj-core-assertion-description .

2
Joel Costigliola