Chapitre Introduction aux tests Unitaires
Pratiques et recommandations
Cette leçon aborde un ensemble de questions assez courantes concernant la pratique des tests.
Pratiques et recommandations
F.I.R.ST
- F - FAST : Les tests doivent s'exécuter rapidement
- I - Independent : Les tests ne doivent pas créer d'effets de bord entre eux
- R - Repeatable : Les tests doivent toujours fournir le même resultat à chaque exécution
- S - Self validating : Les tests doivent valident eux-même le résultat. Aucun log ne doit être consulté pour vérifier son exécution.
- T - Timely : Les tests doivent être écrits avant le code (TDD)
Livre Cleancode by Bob Martin
Cibler la pertinence des fonctionnalités à tester
Chaque user-story ou feature peut être évaluée par :
- Le risque (réel sur l'utilisateur ou environmental ou financier)
- La valeur pour le client (estimée ou indiquée par le client lui-même)
Catégoriser ensuite les features selon la technique de MoscoW

Humm.. Mais j'ai du code Legacy et aucune story
Généralement il est possible de créer des stories ou des tests d'acceptations à partir de :
- la documentation utilisateur
- les spécifications
- un inventaire des écrans si une UI existe
Oui mais j'ai pas le temps, je veux une approche par le code uniquement
L'approche par le code est peut être plus simple à appréhender pour le développeur / testeur mais potentiellement les tests sont plus fragiles.
- sensibilité au refactoring
- difficulté de comprendre l'intérêt fonctionnel du test en cas de changement du domaine
Oui mais quand même, je veux le faire ainsi
Pour rendre les tests utiles et limiter le risque de fragilité, il faut identifier les "interfaces" ou API dans le code.
- les interfaces en Java
- les clusters de classes (groupes de classes indépendants qui possèdent une racine)
- l'API Rest
- les commandes et leurs managers (si le patron de conception Commande est implémenté)
- les méthodes de services Spring si elles ne sont pas trop granulaires
- les patrons de conceptions de l'application MVC, MVVM
Approche par l'instrumentation
- identifier les quick wins au niveau couverture de code avec un outil comme SonarQube (les plus gros morceaux non testés sont ceux avec le plus grand risque de problèmes de maintenance)
- réaliser un profiling de l'application (JProfiler) pour identifier quel code est réellement utilisé par l'utilisateur dans X ou Y scénarios.

Les tests cela coûte cher; on s'arrête quand ?
- Quand on apprend à développer, on teste avec 100% de couverture afin de comprendre que 80% sont suffisants
- On s'arrête de tester, quand les risques (régression, insatisfaction utilisateurs, stabilité du code) sont acceptables et acceptés par le client
- Il vaut mieux peu de tests mais bien compris et qui couvrent une large part de l'application qu'un test unitaire par méthode dans l'application.
Quel est le moyen d’être le plus efficace quand on met en place des tests unitaires ?
Un bon test unitaire ne sauvera pas d'une mauvaise architecture
Corollaire: les applications les mieux testées ont une bonne architecture
Si l'application le permet, il vaut mieux écrire d'abord des tests d'acceptance que des tests unitaires
Utiliser les tests unitaires pour cibler les composants avec le plus de code ou de complexité cyclomatique
Les tests les plus efficaces sont ceux qui s'expriment avec le moins de code, couvrent le plus l'application et dont le résulat attendu est clair.




Faut-il rendre protected des méthodes private pour pouvoir les tester ?
En règle générale, non.
Il faut cibler une méthode publique qui appelle votre méthode privée.
Cas exceptionnel
La méthode peut être un test unitaire pertinent et que les seuls autres moyens de la test est de passer par un test d'intégration, plus fragile de la méthode publique.
Tester entraine-t-il vers des conceptions d’appli particulières ?
ex : méthodes très découpées pour plus de testabilité ?
Oui
Une application bien testée à une architecture bien conçue (designed).
Faut il forcément utiliser la TDD ?
La TDD garantit que le code minimal et suffisant a été développé pour répondre aux tests écrits en amonts. Il ne garantit pas la qualité du code et son élégance. Par conséquent, un code en TDD n'est pas nécessairement bien conçu.
Conseils
Une application testable possède les abstractions nécessaires pour l'écriture des tests d'acceptations, systèmes, d'intégration et éventuellement unitaires.
Tests nécessaires pour garantir sa maintenabilité et satisfaire les exigences utilisateurs.
Quelles sont les abstractions nécessaires ?
Une application facilement testable possède une API métier, exprimant les comportements attendues ou souhaités par l'utilisateur avec des entrées / sorties claires et documentées.
Découper les méthodes en plus petites méthodes n'aide pas si la description fonctionnelle de la méthode est pas claire.
Il est plus facile de tester la méthode Tax::calculerTax(val) : float
Que les méthodes du style :
printResultat(int taux, double val)
Dans quel cas il est crucial de faire des tests ? (pour tous les controlleurs et services ou bien pour des algos ?)
Dans le cas ou votre logiciel peut causer le décès de quelqu'un ou autre conséquence dramatique.

Les tests sont une excellente documentation (à jour) de l'application.
Les tests vous servent d'abord à comprendre ce que fait l'application et pourquoi elle ne fonctionne plus.
D'autres aspects du cours aborde les méthodologies et la priorisation des tests.
Les tests les plus efficaces vérifient des comportements avec des entrées claires et précises et respectent F.I.R.S.T
Est-ce possible de faire des cas « rapides » pour du code existant ?
Les cas rapides sur une application existante dépendent de son architecture et de ses fonctionnalités.
Les meilleurs tests sont :
- Les tests d'acceptations
- Les tests systèmes sur les controlleurs ou les API Rests
- les tests sur les commandes si l'application est implémentée selon CQS

Existe-t-il des astuces pour bouchonner les tests, pour les rendre réutilisables ?

Quand une classe ou une méthode est testée, il importe d'identifier toutes les dépendances nécessaires à l'exécution du SUT.
Les dépendances existent sous deux formes :
- les dépendances privées (on les utilise directement sans Mock ou Stub)
- les dépendances partagées : elles doivent être mockées ou stubbées
Voici quelles astuces pour rendre le code facilement "bouchonnable"
- Utiliser un framework d'IOC
- Utiliser l'injection par constructeurs que par champs ( et reflection)
- Créer une interface au-dessus d'un ou plusieurs composants pour abstraire son utilisation
- Utiliser des frameworks pour émuler le comportement de vos dépendances partagées (HSQLDB, DBUNIT, FAKESMTP, WIREMOCK etc)
Faut-il utiliser des mocks ?
Quelle est la différence entre un spy, un mock et un stub ?
Documentation et resources
Livres intéressants :
- [Unit testing](Principles, practices, patterns)
Sites intéressants