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.