La charte du développement backend chez Deepki - Partie 3, les tests automatisés
Cet article est le troisième d’une série qui détaille la charte du développement backend que nous avons mise en place chez Deepki. Dans cet article, nous abordons en détail le sujet des tests automatisés.
L’importance des tests automatiques
Dans le développement d’une application, les tests automatiques sont indispensables pour garantir la qualité et la stabilité du code. Les tests automatisés permettent de s’assurer que les modifications apportées au code n’ont pas accidentellement altéré son comportement existant.
Cependant, pour que les tests soient efficaces et utiles, il est essentiel de garder à l’esprit les principes suivants :
Tester pour les autres développeurs
L’objectif principal des tests automatisés est d’assurer la stabilité du code pour les autres développeurs. Les tests ne sont pas conçus pour prouver que le code fonctionne, mais pour empêcher les modifications futures de casser accidentellement le code existant.
Les tests sont un filet de sécurité. En tant que tel, il est donc essentiel de tester tous les cas possibles, pas seulement les cas nominaux, et d’inclure les cas “limites” : par exemple, si on teste une librairie de pagination, que se passe-t-il lorsqu’on demande la page 0 ? Ou la page 9999999 ? Ou qu’on demande à afficher -1 objets par page ?
Tous ces tests servent également de documentation sur des comportements prévus.
Garantir des comportements
On écrit toujours un test automatisé pour protéger un comportement contre une altération accidentelle. Il faut donc se poser la question de quels comportements on souhaite garantir.
Le contrat d’interface d’une API web par exemple ne se limite pas toujours à la liste des arguments en entrée et au format de la réponse. On peut également vouloir garantir que lorsqu’on appelle cette API, on émet une métrique, on ajoute une ligne de log, on publie un évènement, on appelle un service externe, etc. Si ces comportements sont importants pour “le monde extérieur”, il faut les protéger et les documenter à travers un test automatisé.
Se découpler de l’implémentation
On a déjà énoncé dans cette charte l’idée que pour garder notre application moderne et facile à maintenir, il est essentiel de la “refactorer” régulièrement. Pour rappel, le “refactoring” consiste à modifier l’implémentation d’une fonctionnalité sans en changer le comportement.
Les tests automatisés doivent être au service du refactoring. En protégeant tous les comportements attendus de l’application, ils offrent au développeur un filet de sécurité qui lui permet de sereinement réécrire les rouages internes de son application en sachant qu’il n’en n’a pas altéré le comportement attendu par “le monde extérieur”.
Mais pour ça, il faut bien entendu que les tests n’utilisent pas ces rouages internes directement, car ce sont typiquement eux qu’on va vouloir remplacer !
Il est donc important de tester tous les comportements de son application, mais de le faire en partant de sa surface d’attaque (qui ne change pas lors d’un refactoring) plutôt que de l’intérieur (qui doit être facile à changer aussi souvent qu’on veut).
Tout ce qui rend le refactoring plus difficile doit être évité, et un test sur une fonction interne qu’on souhaite retirer ou fusionner alors que le comportement global du produit reste le même entre dans cette catégorie.
Écrire des tests rapides, indépendants, autonomes, stables et concis
Les tests automatisés doivent être rapides, indépendants, autonomes, stables et concis pour être efficaces. Voici ce que cela signifie :
- Ils doivent être rapides pour que les développeurs puissent les exécuter fréquemment.
- Ils doivent être indépendants pour que les tests aient le même résultat quel que soit l’ordre dans lequel ils sont lancés. Idéalement, il doit être possible de les lancer en parallèle, ce qui participe à l’objectif de rapidité.
- Ils doivent être autonomes, c’est-à-dire que la lecture du code du test doit suffire à comprendre ce qu’il fait et ce qu’il teste. Quand un test casse, on veut être capable de comprendre pourquoi le plus rapidement possible. Plus le code du test se suffit à lui-même, plus ce sera le cas.
- Ils doivent être stables pour que les résultats des tests soient cohérents : un test qui échoue 1 fois sur 10 sans que le code ait été modifié est très frustrant. Ceci doit être vrai indépendamment de l’environnement depuis lequel les tests sont lancés (laptop, CI, etc.).
- Ils doivent être concis pour être compréhensibles et pour faciliter la maintenance. Il faut cependant trouver le bon équilibre entre concision et autonomie, l’objectif étant toujours que la personne en charge de réparer un test comprenne ce que fait le test le plus rapidement possible.
Qualité du code de test
La qualité du code de test est aussi importante que celle du code de production. Il est donc normal de passer autant de temps à écrire des tests qu’à écrire du code de production, et il n’est pas rare ou choquant que le volume de code des tests soit plus important que celui du code de production.
Couverture de code
Il est souhaitable que les tests automatisés couvrent 100 % du code, mais il ne faut pas le forcer. La couverture n’est pas une mesure de la qualité des tests, car ce qui compte, c’est de faire les bonnes assertions. La qualité des tests ne peut être représentée par un simple chiffre : seul une relecture attentive du code permettra de juger de la qualité des tests produits.
Conclusion
En respectant ces principes, les tests automatisés peuvent aider à améliorer la qualité et la fiabilité du code tout en facilitant le processus de développement et de maintenance.
Cette entrée a été publiée dans programmation avec comme mot(s)-clef(s) programming
Les articles suivant pourraient également vous intéresser :
- Le duel de programmation : Python et Elixir face à face par Yahya Lazaar
- La charte du développement backend chez Deepki - Partie 2, la lisibilité par Olivier Roux
- La charte du développement backend chez Deepki - Partie 1, les grands principes par Olivier Roux
- Pourquoi et comment gérer la compatibilité ascendante ? par Olivier Roux
- Quelques techniques pour rendre un code lisible et agréable (et éliminer la charge cognitive) par Xavier Barbosa
- Débuter avec Ansible par Younes Bentalia
- TypedDict vs DataClass en Python par Pierre Assemat
- Quelques design patterns commentés en python par Vincent Valat
- Au fait, c'est quoi, un algorithme ? par Valentin Leclere
- Les design patterns en Vue3 par Juan Lozano
- This is DOP* par Xavier Barbosa
- The Clean Coder par Robert C. Martin - Être professionnel dans sa pratique du code par Quentin Mondot
- Comment manipuler facilement l'objet datetime dans MongoDB au sein d'un projet international en Python? par Muhui YU
- Introduction au Test-Driven Developement par Alex Peyrard
- Typage en python : comment utiliser le type hinting pour debugger du code par Juan Lozano
- Comment nous gérons nos configurations par Xavier Barbosa
- Domain Driven Design - Définir et modéliser un domaine métier par Younes Bentalia
- L'ETL chez Deepki par Marc Spiteri
- Comment manipuler un DataFrame Pandas avec le MultiIndex ? par Muhui YU
- The Clean Coder par Robert C. Martin - C'est quoi être professionnel quand on est développeur ? par Quentin Mondot
- Comment faire du Parsing de PDF sans utiliser les expressions régulières ? par Bo Jiang
- Dask vs Pandas : Fight ! par Juan Lozano
- Comment faire cohabiter D3 et Vue ? par Matthieu Caule
- Les outils de gestion de packages python par David Couderc
Postez votre commentaire :
Votre commentaire a bien été envoyé ! Il sera affiché une fois que nous l'aurons validé.
Vous devez activer le javascript ou avoir un navigateur récent pour pouvoir commenter sur ce site.