Les tests unitaires en Java

Les tests unitaires en Java

a marqué ce sujet comme résolu.

Tout le monde se secoue ! :D

Il y a longtemps, hedi07 a rédigé un tuto sur le Site du Zéro. Avec son accord, je vais le reprendre.

Mon objectif est de le terminer avant noel, défi de l’avent, tout ça.

L’idée est de passer d’un mini tuto à un tuto moyen, de se baser sur JUnit 5 plutôt que 4, de montrer des exemples plus construits tout en restant très noob-friendly (le principal intérêt du tuto initial).

J’ai donc refait le plan complet et à l’heure actuelle, seul la première section du premier chapitre est finie. Je fais cette béta pour avoir quelques retours des afficionados de java et surtout pour avoir votre avis sur les améliorations à apporter au tuto initial.

À présent, c’est à vous ! <-

Merci !

+4 -0

Salut,

Déjà, je pense que ce genre de tuto serait très utile ! Je ne peux donc que le soutenir.

Quelques remarques au fur et à mesure de ma lecture :

  • Définitions et utilité :
    • Je pense honnêtement que le début passe à côté des plus gros intérêts des TU, et en particulier que le tableau est faux. S’ils sont commités avec le code, des tests faits avec des main peuvent tout à fait être reproductibles, compréhensibles et documentés – j’ai même croisé un très vieux projet avec plus de 500 main de test dedans. Par contre, c’est ultra pénible parce que c’est spécifique au projet, éparpillé, mélangé avec le code de production, difficile à découvrir et surtout, surtout, impossible à lancer en tant que « suite de tests ». Ça ne produit pas non plus de rapports standards à intégrer dans un flux de travail sain (intégration continue, etc).
    • Pour moi il manque aussi une notion importante : avoir des TU, c’est aussi faire en sorte que le code soit architecturé de façon à être testable. Toute personne qui a tenté d’ajouter des tests à postériori sur un code historique voit très bien ce dont je veux parler.
  • Mise en pratique :
    • Pinaillage : JUnit est un framework de test de Java, pas le seul existant.
    • Pour l’installation, ça serait pas mal de renvoyer vers les documentation pour Maven et Gradle.
  • Remplir les méthodes de test :
    • Je ne vois vraiment pas l’intérêt de présenter les tests avec fail(), je ne connais personne qui fait ça – de fait, l’utilisation de fail() en-dehors du cas d’un test pas encore implémenté est très rare. Pour moi il vaut mieux présenter directement les assertions avec les méthodes assertXXX(), parce que ça correspond bien mieux à ce qui se fait (donc évite de commencer par apprendre quelque chose d’inutilisé), et surtout permet de montrer dès le début qu’une méthode de test peut être simple (c’est important pour faire accepter l’idée de passer du temps à faire des tests).
  • La couverture du code :
    • Pourquoi ce paragraphe est sur Eclipse et pas sur IntelliJ ?
    • Je présenterais les différentes notions de couverture de code, en particulier celle par lignes (qui est celle par défaut d’IntelliJ) et celle par instructions, et qui explique pourquoi on peut avoir un 100 % de couverture affiché mais quand même des cas non couverts.
    • Je préciserais aussi explicitement que « 100 % de couverture de code ne garantit pas que le programme n’a pas de bugs ».
  • Tester proprement : la gestion du contexte
    • Là aussi ça parle d’Eclipse (une conversion en cours de ta part ?)
    • Je vois beaucoup trop de champs en static, c’est un coup à avoir des problèmes de tests (ou pire, des tests qui tombent en marche) par effet de bord sur ces valeurs ; ou à avoir des procédures de setup/teardown complexes et fragiles. Tout ce qui n’est pas impérativement une variable de classe devrait être une variable d’instance. C’est d’autant plus important que JUnit ne garantit pas la stabilité de l’ordre des tests, mais que celui-ci est généralement stable sur une même machine, ce qui complexifie encore les débuggages de ce genre. Enfin, ça évite les pré-assertions (comme le assertEquals(expectedSize, sut.getSize());) pour vérifier l’état de l’objet avant le test.
  • Les mocks :
    • Je ne sais pas si c’est pertinent de présenter les mocks comme étant des implémentations ad hoc des interfaces. Je n’ai jamais vu personne faire ça dans toute ma carrière (je le vois très souvent sur des interfaces extérieures au programme – typiquement les entrées/sorties –, mais jamais sur les interfaces Java au sein du projet). Par contre, présenter directement des outils comme Mockito peut être trop difficile.
  • Conclusion :
    • « En général, dans une équipe de développeurs, ce ne sont pas les mêmes personnes qui testent et qui développent. ». Heu, si. On parle de tests unitaires ici, qui sont normalement du ressort des développeurs (peut-être pas sur d’énormes organisations qui ont un système de test-driven development très poussé ?), mais le cas général c’est que les développeurs font aussi leurs propres TU. J’ai même plutôt tendance à me méfier du cas où les développeurs ne font pas leurs TU, parce que ça veut souvent dire qu’il n’y en a pas du tout… Les tests d’intégration c’est différent, là c’est souvent d’autres personnes qui s’en chargent.

Je pense aussi qu’il manque aussi deux choses, dans ce qui me vient immédiatement à l’esprit :

  1. On peut organiser son code de test en trois sections : sachant que […] lorsque […] alors […]. Les trois sections délimitent l’état du système avant le test, la procédure testée (normalement une seule ligne sauf contraintes techniques) et les vérifications.
  2. C’est pas vraiment un manque mais une organisation du texte : la question de « ce qu’il faut tester » (les cas nominaux, les cas limites, les cas d’erreurs, les entrées aberrantes) est éparpillée dans « Les problèmes des tests » et mériterait d’être plus explicite.

Merci pour ce retour.

D’une manière ou d’une autre il sera totalement pris en compte. Juste pour comprendre pourquoi le texte actuel est comme ça : je reprends le vieux tutoriel. Il se peut que l’immense majorité du texte soit revue, mais dans mon travail préliminaire, j’ai repris le texte original en adaptant à JUnit 5.

Notons que le but de ce tuto est de démarrer avec une personne qui débute dans l’apprentissage du test, pas une personne qui travaille déjà en entreprise et qui se projette dans un base de code complexe.

Comme je le disais l’ensemble des remarques que tu as faite seront prises en compte, je ne sais pas encore comment exactement car il faut être progressif vu le public du tutoriel. Sinon autant faire un lien vers baeldung.

Je pense honnêtement que le début passe à côté des plus gros intérêts des TU, et en particulier que le tableau est faux. S’ils sont commités avec le code, des tests faits avec des main peuvent tout à fait être reproductibles, compréhensibles et documentés – j’ai même croisé un très vieux projet avec plus de 500 main de test dedans. Par contre, c’est ultra pénible parce que c’est spécifique au projet, éparpillé, mélangé avec le code de production, difficile à découvrir et surtout, surtout, impossible à lancer en tant que « suite de tests ». Ça ne produit pas non plus de rapports standards à intégrer dans un flux de travail sain (intégration continue, etc).
Pour moi il manque aussi une notion importante : avoir des TU, c’est aussi faire en sorte que le code soit architecturé de façon à être testable. Toute personne qui a tenté d’ajouter des tests à postériori sur un code historique voit très bien ce dont je veux parler.

Pas encore pris en compte, ça viendra, mais je veux me poser pour bien faire ça.

Pinaillage : JUnit est un framework de test de Java, pas le seul existant.

Fixed

Pour l’installation, ça serait pas mal de renvoyer vers les documentation pour Maven et Gradle.

Je verrai comment faire.

Je ne vois vraiment pas l’intérêt de présenter les tests avec fail(), je ne connais personne qui fait ça – de fait, l’utilisation de fail() en-dehors du cas d’un test pas encore implémenté est très rare. Pour moi il vaut mieux présenter directement les assertions avec les méthodes assertXXX(), parce que ça correspond bien mieux à ce qui se fait (donc évite de commencer par apprendre quelque chose d’inutilisé), et surtout permet de montrer dès le début qu’une méthode de test peut être simple (c’est important pour faire accepter l’idée de passer du temps à faire des tests).

J’ai totalement revamp cette partie là. Je pense que ça permet d’aller petit à petit vers le concept sans jeter fonctions que le débutant ne peut pas deviner de suite ni pourquoi elles sont là.

Pourquoi ce paragraphe est sur Eclipse et pas sur IntelliJ ?

Car je n’ai pas encore eu le temps de le reprendre, comme je le disais, je n’ai pu revamp qu’une toute petite partie du tuto et que pour moi le coverage n’a pas sa place aussi tôt dans le tuto.

Je ne sais pas si c’est pertinent de présenter les mocks comme étant des implémentations ad hoc des interfaces. Je n’ai jamais vu personne faire ça dans toute ma carrière (je le vois très souvent sur des interfaces extérieures au programme – typiquement les entrées/sorties –, mais jamais sur les interfaces Java au sein du projet). Par contre, présenter directement des outils comme Mockito peut être trop difficile.

Le chapitre des mocks DOIT être revamp, ne serait-ce que parce qu’il passe à côté de cette si loufoque et géniale traduction francophone "le bouchonnage".

Blague a part, le tutoriel initial est vieux, je le reprends car j’aime pas trop l’idée de perdre le référencement, mais certaines parties seront jetées et refaites alors que sur d’autres je vais par exemple garder la trame voire les idées d’exemples.

Pour les connaissances plus générales à propos des tests, notamment critères de couverture, j’ai un cours que j’ai repris d’un collègues qui contient pas mal d’information et de références. Notamment si tu veux lister un peu plus en détails les critères de couvertures qui existent.

Je ne sais pas si ça peut t’intéresser ?

Ce sujet est verrouillé.