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

90

Nous avons une suite de tests qui utilise principalement les assertions JUnit avec les matchers Hamcrest. Un membre de notre équipe a commencé à expérimenter avec AssertJ et a impressionné les gens par sa syntaxe, sa flexibilité et son caractère déclaratif. Il existe une fonctionnalité fournie par JUnit 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 conçus pour la lisibilité humaine et qui auront des identifiants ou des UUID apparaissant au hasard 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, dans le cadre de l'objectif qu'elle remplit est de mapper des données entre d'autres services sans nécessairement comprendre de quoi il s'agit.

Dans JUnit, la assertThatméthode fournit une version avec un String reasonparamètre avant le Matcher<T>param. Cela rend trivial l'ajout d'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 jillion de méthodes génériquesstatic assertThat différentes qui renvoient une certaine forme d' interface Assert ou l'une de ses nombreuses classes d'implémentation. Cette interface ne fournit pas de méthode standard pour définir un message personnalisé à inclure avec 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?

Patrick M
la source

Réponses:

137

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

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

Définit la description de cet objet prenant en charge la String.format(String, Object...)syntaxe.
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 hasMessageest ce qui apparaît dans le journal de sortie de votre test unitaire si l'assertion échoue.


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

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

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

La page d'accueil d'AssertJ ne mentionne aucun des deux assistants jusqu'à la page de mise en évidence des fonctionnalités, qui montre des exemples de l' asassistant dans la section Assertions souples , mais ne décrit pas directement ce qu'il fait.

Patrick M
la source
22

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 Descriptable.asest qu'elle vous donne un contrôle total sur le message personnalisé - il n'y a pas de "attendu" 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 affirmation réelle - sinon cela ne fonctionnera pas, car l'assertion se déclenchera en premier. Ceci est noté dans le Javadoc.

sleske
la source
3
"vous devez appeler withFailMessage () avant toute affirmation réelle" merci, cela m'a trébuché. L'ordre d'appeler la withFailMessagematière; J'aime AssertJ, mais ça craint.
Abhijit Sarkar
0

Utilisez la as()méthode intégrée dans AssertJ. Par exemple:

 assertThat(myTest).as("The test microservice is not active").isEqualTo("active");
Testilla
la source