shutterstock_515811103

Vous moquez-vous des tests?

Je me suis récemment « entretenu » avec le moi d’il y a plusieurs années, et nous en sommes arrivés à parler de tests unitaires. À cette époque, je me moquais des tests unitaires. Aujourd’hui, je m’en moque encore, mais de façon différente. Malgré les années passées, plusieurs des questions soulevées au cours de cette conversation sont encore d’actualité. Voulez-vous savoir comment celle-ci s’est déroulée? La voici, agrémentée d’extraits de code pour bien montrer mon cheminement…

Tu sais, tu devrais commencer tout de suite à mettre un peu plus l’accent sur les tests. Ce n’est pas juste une mode, cela va rester et crois-moi, c’est beaucoup plus important que tu peux le penser.

 

Je m’en moque des tests unitaires! Nous avons une équipe d’assurance qualité qui s’occupe de vérifier que nos applications fonctionnent.

 

Mais combien de fois est-ce que cela revient dans ta cour pour des « petits bogues »? Les tests unitaires auraient permis d’éviter cela, et l’équipe d’assurance qualité aurait pu se concentrer à vérifier le fonctionnement de l’application, et non voir si elle est brisée ou non.

 

Mais Bruno, c’est compliqué d’écrire des tests unitaires. Comment est-ce que je peux tester une fonction comme celle-ci?

test_1

 

Effectivement, tester une fonction comme celle-là, c’est un défi, parce qu’elle en fait beaucoup plus qu’elle devrait. La fonction crée ou met à jour un client, calcule le prix selon les taxes, et finalement sauvegarde la facture. Malgré qu’elle soit nécessaire dans le processus, toute cette logique n’a pas sa place ici. Tu pourrais déplacer ce qui concerne le client et les taxes dans leur propre classe, pour que chacune s’occupe d’une seule chose. Cela nous permettrait de respecter le principe SOLID de responsabilité unique qui dit qu’une classe ne devrait avoir qu’une seule responsabilité. De cette manière, pour ce qui est de tester cette méthode, on pourra se concentrer à vérifier qu’elle fait bien son travail, c’est-à-dire orchestrer le tout.

 

J’ai suivi ta recommandation. La logique pour créer ou mettre à jour le client est encapsulée dans sa classe, et j’ai maintenant une classe pour calculer mon prix. Par contre je ne vois toujours pas comment cela peut m’aider pour mes tests, je n’ai pas de contrôle sur ces objets à l’intérieur de ma méthode… Qu’en penses-tu ?

Test_2

 

Tout à fait d’accord avec toi, ce n’était que la première étape. Nous allons parler d’un autre principe SOLID, celui de l’inversion de dépendance. Le problème actuel, qui est probablement la principale raison pour laquelle tu as de la difficulté à tester, c’est que ta classe dépend de ces autres classes pour fonctionner. Donc, si plutôt que de dépendre de ces classes, tu dépendais d’interfaces? Tout ce dont ta classe aurait besoin, c’est de recevoir ces interfaces, dans le constructeur par exemple, et c’est l’appelant de celle-ci qui s’occuperait de créer les objets qui implémentent ces interfaces.

 

Bon, d’accord. J’ai fait un peu de « refactoring ». Mes classes sont maintenant des instances d’interfaces qui sont passées dans le constructeur de ma classe. Mais je dois quand même les créer pour tester ma méthode, alors si tu veux mon avis, j’ai juste déplacé le problème…

Test_3

 

Nous arrivons à la partie intéressante : les « mocks ». En gros, ces objets sont des simulateurs de comportement pour un objet donné. Nous pouvons donc dire à notre simulateur comment nous voulons qu’il se comporte, histoire de nous permettre de tester autre chose en fonction de ce comportement. Si nous reprenons ton exemple, cela peut donc nous permettre de simuler un retour de la base de données pour un nouveau client, comme s’il avait véritablement été créé.

 

Et comme nous contrôlons cette valeur, nous pouvons facilement vérifier que c’est bien celle-ci qui a été envoyée pour la sauvegarde de la facture. Par exemple, si tu voulais vérifier qu’il n’y a pas de calcul de taxes et de sauvegarde quand tu n’as pas de numéro de client, ton test pourrait ressembler à ceci :

test_4

 

Et en plus, maintenant que chacune de tes trois classes est bien isolée, tu vas voir que cela devient plutôt facile de tester leurs comportements individuellement.

 

Là je comprends! C’est super! Donc maintenant que je sais que tous mes morceaux fonctionnent bien, je peux être sûr que mon application va toujours bien fonctionner, n’est-ce pas? Plus besoin de faire de l’assurance qualité à ce propos?

Si seulement c’était aussi simple… Effectuer des tests unitaires sur des classes qui n’ont qu’une seule responsabilité, en simulant le comportement de leurs dépendances, est déjà un grand pas en avant. Mais cela ne fait que tester chaque composante. Imagine que le constructeur de ton automobile avait testé que les freins fonctionnent, que la pédale peut être enfoncée, mais pas le fonctionnement conjoint des deux éléments. Aurais-tu confiance en ta voiture? Et bien la réponse est la même du côté logiciel. Les tests d’intégration, qui sont essentiellement des tests automatisés pour des scénarios bien précis sur un environnement complet, et les tests manuels effectués par de vraies personnes, sont quand même nécessaires.

 

Je comprends, il faut donc continuer les tests manuels, mais quand même mettre l’accent sur les tests unitaires. Au moins, je sais maintenant comment attaquer le tout pour remanier mon code.

 

Nous avons parlé de ta méthode qui était déjà en place, que nous avons fait évoluer vers quelque chose d’un peu mieux structuré. D’ailleurs, ce qui est bien là-dedans, c’est que non seulement tu peux conduire tes tests unitaires plus facilement, mais en plus nous avons grandement amélioré la structure de ton code en fonction de principes fondamentaux de programmation. Et dis-toi que tu pourrais même faire cela pour du nouveau code, pas uniquement pour des choses existantes. Cela ne peut que t’aider dans ta carrière…

 

Et si nous avions plus de temps, nous pourrions parler de Test Driven Development, où tu écris carrément les tests avant d’écrire le code, et tu construis graduellement ton code fonctionnel au fur et à mesure que tu en as besoin au niveau des tests. Nous aurions aussi pu parler de « fakes » qui sont des classes concrètes qui renvoient des données précises sans suivre la vraie logique en arrière-plan, ou de « stubs » qui sont en quelque sorte l’ancienne manière de faire des mocks. Même si selon moi, il vaut mieux te concentrer sur les mocks.

 

Cela m’intéresse, mais effectivement il ne nous reste plus beaucoup de temps. Il serait bien de trouver un autre moment pour se parler. Et dis, tant qu’à parler avec mon futur moi, est-ce que tu pourrais me donner un petit scoop sur ce que je vais faire?

Cela risque de créer un problème avec le continuum espace-temps, mais pourquoi pas… Tu vas être coanimateur d’une chaîne sur YouTube, le Bracket Show, qui parle de questions reliées au domaine du développement logiciel. Mieux que ça, tu vas même faire un épisode sur ce dont nous venons de parler :

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *