Problèmes courants dans l'écriture de tests
Sur un précédent projet, la plupart des membres étaient nouveaux et peu familiers avec l'écriture de tests. Cela a conduit à deux problèmes récurrents : des noms de tests vagues et un code de test difficile à lire.
Des noms de tests imprécis
Les développeurs débutants ne pensent pas naturellement au triptyque Given-When-Then. Ils se contentent souvent d'un simple should_xxx, sans préciser les conditions. Résultat : le nom du test ne décrit pas ce qu'il vérifie.
J'ai découvert sur StackOverflow un modèle intéressant : GivenA_WhenB_ThenC. Je l'ai adapté avec deux améliorations :
- Supprimer les mots-clés : on peut omettre "Given" et "When" si la convention est respectée (le premier segment est le Given, le deuxième le When, le dernier le Then).
- Séparateurs : le camelCase devient illisbile quand le nom est long. J'utilise
___(triple underscore) comme séparateur. Deux underscores sont trop courts, quatre sont inutiles.
Exemples tirés d'un exercice DDD :
parking_order_is_natural_order___park_cars_by_parking_assistant___car_parked_to_correct_parking_lot_in_turn
car_is_already_took___take_back_car_with_used_receipt___exception_is_thrown
parking_lot_is_available___park_a_car_by_parking_assistant___receipt_returned
car_is_parked___take_back_car_with_invalid_receipt___exception_is_thrown
Astuce : Si le Given est absent ou trivial, on peut le sauter mais le séparateur reste obligatoire : ___xxx_xxx___xxx_xxx.
Bénéfices
- Uniformisation des noms de tests dans l'équipe (langage commun). On peut ajouter une vérification statique pour détecter les noms non conformes.
- Obligation de réfléchir au contexte (Given), à l'action (When) et au résultat attendu (Then).
- Lecture facilitée : le cerveau distingue immédiatement les trois parties sans avoir à interpréter.
Inconvénient
Les noms deviennent longs. Il faut apprendre à les conciser en évitant les répétitions.
J'appelle cette approche la nommage GWT (Given-When-Then).
Code de test illisible
Beaucoup de développeurs créent des méthodes createX() qui masquent toute la construction, y compris les sous-objets :
Foo createFoo() {
Bar bar = new Bar();
return new Foo(bar, valeur1, valeur2);
}
En lisant un test qui utilise createFoo(), on ne sait pas comment valeur1 et valeur2 sont définies. Modifier cette méthode peut casser plusieurs tests de façon inattendue.
Ma solution actuelle :
- Exposer explicitement les données importantes dans le test. Par exemple, si
valeur1etvaleur2influencent la logique, passez-les en paramètre directement :
@Test
void ___quand_voiture_deja_prise___recuperer_avec_ticket_utilise___exception_levee() {
Bar bar = creerBar();
Foo foo = creerFoo(valeur1, valeur2, bar);
// agir
// vérifier
}
Avantage : on voit immédiatement les données sans avoir à naviguer dans le code.
- Ne lister que les données qui comptent pour le test. Si un champ n'est pas utilisé dans la logique testée, donnez-lui une valeur par défaut dans la méthode de construction.
Pourquoi ne pas utiliser un builder ?
J'ai déjà essayé les builders, mais le code devient verbeux. La méthode createFoo(paramètres) est plus concise et facile à lire.