Mon premier billet sur les tests présente le concept et comment, avec peu d’effort, vous pouvez améliorer ce que vous faites déjà. Mais en conclusion, je vous affirme que ce premier jet de tests n’est pas une bonne pratique. Il y a plusieurs raisons et dans ce billet, je vais aborder la question de quoi tester. En particulier pour le langage Python qui a une manière particulière de gérer la visibilité des attributs.

Notez que le concept présenté ici s’applique à tous les langages.

Rappel du contexte

Pour rappel, l’exemple était une gestion de tâches. Un des tests concerne la validation que l’ajout d’une tâche ajoute bien une tâche. Pour ce faire, l’assertion vérifie l’état de l’attribut _tasks. Or, cet attribut est privé. Oui, en Python tout est public et on est tenté d’utiliser cette propriété dans un cas comme celui-ci.

Le test d’ajout de tâches était donc le suivant :

Pourquoi un attribut est-il privé ?

Définir un attribut comme privé (je parle ici de Python, langage pour lequel la notion d’attribut concerne aussi bien les attributs que le méthodes qui sont des attributs callable) sert avant tout à mettre en œuvre le principe de l’encapsulation, c’est à dire de masquer l’implémentation. Masquer ne signifie pas dissimuler, surtout pour un langage interprété. Si on souhaite masquer l’implémentation, c’est pour pouvoir faire évoluer cette implémentation sans casser l’usage de notre code.

Ainsi, dans notre implémentation, _tasks est une liste, la liste des tâches. Imaginons maintenant que nous souhaitons faire évoluer la gestion en ajoutant une notion de priorité. La priorité serait un attribut optionnel de l’ajout d’une tâche. Dans notre code, nous pourrions imaginer que la gestion de la priorité sera gérée par collection dédiée. Ainsi, _tasks ne sera par exemple plus une liste mais un dictionnaire avec pour clef la priorité.

Le code de l’objet de gestion des tâches pourrait évoluer dans ce sens (seul le code d’intérêt est produit ici) :

Si nous rejouons les tests, l’ajout des tâches est toujours d’actualité. Mais la validation, qui vérifie la taille de l’attribut _tasks considère maintenant l’état comme faux…

Vérifiez l’état, pas l’implémentation

Vous voyez que le non respect du principe de l’encapsulation a des effets tout aussi néfastes sur les tests que sur du code fonctionnel. Vérifier le bon fonctionnement de la méthode d’ajout nécessite donc de pouvoir observer quelque chose. Ce quelque chose peut avoir plusieurs formes : l’objet TaskManager peut être implanté comme une collection avec une méthode spéciale len, proposer une property tasks retournant la liste de toutes les tâches ou une méthode next_task(self) retournant la tâche suivante, qu’il faudra donc appeler un nombre déterminé de fois.

Personnellement, j’opterai pour la première possibilité, ce qui conduira l’implémentation suivante

à devenir

Mais le test a quand même produit un échec…

Oui, c’est exacte. Modifier le type utilisé pour définir la taille de la collection va entrainer un échec du test. Mais observez bien la différence.

Avec l’approche où nous allions chercher l’attribut privé, nous aurions dû modifier la logique du test. Ne plus évaluer l’attribut _tasks mais reproduire la logique.

En accédant à la taille de la collection, nous mettons avant tout en évidence que la méthode spéciale, avec l’évolution de l’attribut, doit elle aussi évoluer. Si le test initial (avant modification de la méthode len) est en échec, c’est bien parce que nous avons une régression dans notre code. Ce test nous met ceci en évidence et nous rappelle de modifier ce code afin que l’objet conserve un comportement cohérent.

En choisissant correctement vos tests, vous vous protégez donc contre la régression.

Attention sur le choix des observables. Je lis régulièrement qu’un code soit être testable. La perversité est d’adapter le code au test. Dans le cas présent, n’ajouter pas d’accesseur parce que vous en ressentez le besoin pour les tests. Tout attribut et méthode doivent être définis pour un besoin fonctionnel. Ce que vous devez tester, c’est le bon fonctionnement de votre code pour les services qu’il doit vous rendre et donc pour l’objet, par les interfaces publiques.

Prochaine étape, l’organisation du code.

À propos de... Darko Stankovski

iT guy, photographe et papa 3.0, je vous fais partager mon expérience et découvertes dans ces domaines. Vous pouvez me suivre sur les liens ci-dessous.