TP Qualité de dev.
S. Salva

TP1 Tests Unitaires


1. Objectifs

Rappel ?
Savoir développer des tests unitaires avec JUNIT, Hamcrest, etc.
Calculer la couverture de test d'une application
Appréhender le TDD

2. Un cas de test helloworld en JUNIT

A

Vous savez faire des tests depuis plusieurs mois. Voyons voir :
  1. avec votre IDE favori, créez un projet Java et une classe Helloworld qui contient une méthode qui retourne une chaine
  2. créez 2 test cases basiques pour cette classe (donc 2 fichiers de test)
La liste exaustive des assertions est disponible sur ce lien.

B

Créez une suite de tests (ensemble de cas de tests). Voir ici.
Si vous n'avez jamais, voilà 2 possibilités :
1 : Cas simple (non portable) IntelliJ : faites plusieurs répertoires de test...
2 : Utilisation de @Suite avec Junit. Voilà un exemple :

test
        suite
Pour tester, faites une suite de test qui contient vos 2 test cases précédents.

Vous pouvez aussi faire exécuter vos test cases en parallèle avec l'annotation @Execution(CONCURRENT). Testez !

3. Une introduction aux test driven development TDD

Source Wikipedia: Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards. Kent Beck, who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple designs and inspires confidence.

Intuitivement, le développeur crée donc les tests avant de coder les classes petit à petit. Ca permet de gagner en bonnes pratiques et d'obtenir du code testé de façon incrémentale.

A

L'objectif est ici d'implanter une classe ManipulationString qui contient une méthode sum qui retourne la somme du code ASCII de chaque caractère d'une chaine.

Commencez par créer un cas de test. Cette fois, au moment de la création, pensez bien à utiliser les fixtures (annotations du type @BeforeEach)
et ajoutez une déclaration private ManipulationString ms dans le code du cas de test si ce n'est pas fait.
Créez un cas de test pour vérifier la somme :

int expected = 100;
assertEquals ( expected , ms. sum("d"));
expected = 265;
assertEquals ( expected , ms. sum("Add"));

Que mettre dans les méthodes annotées par @BeforeAll et @BeforeEAch ?

Lancez le test case. Evidemment ça ne marche pas, la classe n'étant pas implantée.

B

Implantez la méthode sum() pour qu'elle valide le cas de test. Vérifiez que vous arrivez au bon résultat en exécutant le cas de test de façon itérative.

C

Ajoutez un test avec:

int expected = 0;
assertEquals ( expected , ms. sum(""));

Et oui c'est un cas à ne pas oublier.
Lancez le cas de test -> Retourne-t-il Fail ? Corrigez le code de la méthode jusqu'à que le cas de test retourne PASS.

Le TDD est sur ce principe, vous ajoutez une fonctionnalité au cas de test et vous codez en vérifiant grâce au cas de test (mais vous le savez déjà ?).

4. Gestion des exceptions

A

Commençons à gérer les exceptions. Complétez le cas de test avec l'appel à la méthode ms.sum(null);. Le comportement souhaité est le suivant : si une exception est levée dans le cas de test, celui-ci doit retourner FAIL. Comment faire ?

Lancez le cas de test. Et corrigez le code source du projet pour finalement obtenir PASS.

B

Cette fois, on veut modifier le comportement de fonctionnement de la méthode sum(). Il vous ai demandé à ce que la méthode sum() renvoie l'exception java.security.InvalidParameterException lorsque le paramètre "" est donné. Ajoutez une annotation sur le test précédent pour qu'il soit ignoré. Puis ajouter un test pour vérifier que si l'exception InvalidParameterException est reçue, alors le test est PASS.

Créez un test supplémentaire pour faire la même chose mais avec l'assertion assertThrows

Modifiez le code de la fonction sum() pour obtenir PASS.


Suivant l'IDE, Si vous trouvez barbant de relancer vos tests à la main, vous pouvez installer Infinitest (pour IntelliJ voir ici)

Infinitest recherche les tests dans un projet et les lance automatiquement au moindre changement.

5. Couverture de code

La couverture de code consiste à estimer le pourcentage de lignes de codes ou de classes ou de composants exécutés par vos tests. C'est une donnée très importante pour estimer la qualité de développement.

Si vous utilisez IntelliJ,

Attention: ce qui nous intéresse, c'est la couverture de la classe ManipulationString uniquement. Modifiez vos options si d'autres classes sont prises en compte. ATTENTION: IL NE FAUT PAS COUVRIR LES TESTS !

Sous intelliJ, voilà un exemple :
couverture

Dans votre cas de test, commentez la partie de code relative au test de l'exception et relancez les tests. Maintenant dans la classe ManipulationString, vous verrez que l'exception est en rouge.

6. Hamcrest

Comme dit dans le cours, la librairie Hamcrest vous offre la possibilité de rendre vos tests plus lisibles ( et élégants).

Créez un test case et refaites vos tests en utilisant explicitement Hamcrest (voir https://junit.org/junit5/docs/snapshot/user-guide/#writing-tests-assertions-third-party ) et surtout https://hamcrest.org/JavaHamcrest/tutorial