Dans cette nouvelle série d’articles, plus orientée vers la technique et le processus de développement, François nous partage sa rencontre et ses réflexions avec les tests.
Cette réflexion comporte deux parties : la découverte des enjeux de tests, objet de cet article, et l’approche méthodique de réalisation que vous trouverez dans le second article.
Initiation aux tests de code
Je me souviens du jour où j’ai découvert les tests.
Le concept m’a d’abord paru étrange.
Ecrire du code qui teste le code ?
Une question stupide m’a traversé l’esprit.
Oui, mais alors, comment tester le code qui teste le code ?
Puis, j’ai été séduit par l’idée.
Ok, j’en avais compris les intérêts dans les grandes lignes :
- S’assurer qu’un code fonctionne tel qu’on s’y attend,
- Vérifier qu’un code fonctionne aux bornes du système,
- S’assurer qu’un changement n’entraîne pas une régression,
- Automatiser une tâche répétitive donc chiante,
- Être beaucoup plus serein pendant les phases de refactoring,
- Rentrer chez soi avec la satisfaction d’un travail abouti.
Il ne restait plus qu’à la mettre en pratique. Je me suis alors jeté dans le grand bain, sans expérience et sans réel approfondissement du sujet.
Erreur, que j’allais payer rapidement.
Très vite confronté à des problèmes divers qui ralentissaient considérablement ma capacité à produire le « vrai » code, je devais réfléchir autrement.
J’ai pas le temps de faire des tests, on devrait les inclure à nos chiffrages.
Echec total, renoncement, rejet d’un concept qui semblait prometteur.
Certes les tests prennent du temps, mais correctement implémentés, en font gagner beaucoup plus.
Mes erreurs
J’ai pu rencontrer, lors de conférences, des développeurs manifestement conquis par les tests. Il était évident que j’étais passé à côté de quelque chose.
L’idée m’avait semblée pourtant bonne.
Comme j’ai peu de temps, je vais traverser la stack de bout en bout pour la tester dans son intégralité.
D’où la création de tests qui lanceraient des requêtes http et feraient des assertions sur le contenu retourné. Si le contenu retourné est celui attendu, j’extrapole le fait que les couches intermédiaires ont correctement fonctionné. Génial ! Pas tant que ça…
Premiers points gênants :
- Ca ne peut s’appliquer qu’à un environnement web,
- Si je veux réutiliser la couche métier dans un autre projet, je n’ai plus aucun test,
- Les données d’entrée ne peuvent être envoyées que par http,
- Les assertions sont complexes à réaliser car je dois au préalable parser le retour html.
De plus, comme l’ensemble de la stack est traversé, il est indispensable que toutes les couches soient présentes. L’environnement de test n’étant pas un environnement d’exécution « normal » (comme dev ou prod), je dois simuler certaines couches (cf. mock). Dans mon cas, par exemple, la session utilisateur.
Enfin, pour pouvoir faire des assertions correctes, il faut également maîtriser le jeu de données initiales. Or, je n’ai pas d’accès direct à la couche métier. Je suis donc dans l’obligation de créer une base de données spécifique à l’environnement de test.
Avant d’avoir commencé, on voit déjà beaucoup de complications apparaître.
Une fois l’environnement en place, un autre problème est rapidement apparu. Les données de test étant centralisées dans une base de données, elles devenaient communes à l’ensemble des tests.
Un test qui modifiait l’état de cette base impactait directement les tests suivants !
Nouveau cap : réinitialiser la base de données
En conséquence, j’ai dû réinitialiser la base de données avant chaque test. C’est bourrin et très inefficace ! Malgré cette réinitialisation systématique, mes tests étaient toujours très dépendants les uns des autres. L’insertion de données initiales dans la base impactait également l’ensemble des tests.
Il devenait très compliqué de modifier ou d’ajouter un test.
L’environnement de test doit être simple à mettre oeuvre, l’ajout d’un test doit être une opération triviale et rapide. Si ce n’est pas le cas, vous constaterez un désengagement des développeurs à les maintenir et à les faire évoluer.
Le package « tests » est devenu peu à peu une ville fantôme où n’erraient plus que quelques âmes perdues.
Ce type de tests est appelé tests fonctionnels car ils s’emploient à vérifier le bon fonctionnement général d’une application tel que le ferait un humain.
Ce sont ce qu’on pourrait appeler des tests de haut niveau.
D’autres tests de ce genre vont jusqu’à reproduire automatiquement le comportement d’un utilisateur directement depuis un navigateur (cf. Selenium).
Est-ce que les tests fonctionnels sont intrinsèquement mauvais ?
Je ne pense pas.
Ils peuvent être très adaptés pour tester les retours d’une api rest, ou dans le test d’interfaces. Mais pas pour une couche métier.
Retour à la case départ, je devais trouver un socle de test plus en adéquation avec mon besoin. Je devais voir plus petit.