’article d’introduction débute en listant certaines différences de visions que je peux avoir avec d'autres développeurs concernant l'architecture applicative ou encore la rédaction des tests. À travers elles, j’évoque les difficultés qu’ils peuvent rencontrer à identifier précisément quoi tester et comment.
Nous avons pu voir dans ces articles autour des tests unitaires :
Que ces tests sont exclusivement centrés sur les règles métiers qui sont validées via de fausses implémentations.
À un moment, il va falloir réaliser des implémentations afin de pouvoir sauvegarder en base de données, envoyer un SMS ou encore afficher des données à l'utilisateur.
Ici, nous allons nous focaliser sur les tests d'intégration des adaptateurs de droite ou encore adaptateurs secondaires, il existe d'autres synonymes. Pour connaître leurs particularités, je vous conseille ces articles :
Dans l’article : Un test peut en cacher un autre — Tests d’intégration — P1 nous nous attardons davantage sur les tests des adaptateurs de gauche.
On va travailler avec un Kata qui sera la gestion d’un système de réservation de livres dans une librairie, dont les règles sont les suivantes :
NDR : le code est en TypeScript, qui propose un typage statique fort. Néanmoins, le code peut être transposé dans des langages comme JavaScript, Python ou PHP qui ont nativement un typage dynamique et faible pour certains.
Ces tests ont pour but de s’assurer que les implémentations choisies s'intègrent bien avec nos Use Cases et respectent bien un port défini (le contrat). Ils ont en point d’entrée l’Adaptateur. Ils sont fortement couplés à un choix d'architecture (librairie, framework, technologie, etc.).
On fera de manière intelligente un ou quelques cas passant et non passant, si ces derniers existent. Le tout en évitant de re tester les règles métiers, car c’est déjà fait ! On souhaite uniquement savoir si l'interaction avec la base de données choisie fonctionnent, que notre appel HTTP s'effectue bien, etc.
Je distingue deux façons (doublage du tiers et utilisation du tiers) de tester avec chacune leurs avantages et inconvénients et il en existe certainement d'autres en fonction des écosystèmes.
Dans cette façon, on va faire des interactions “fictives” avec le tiers car on va substituer le client pour tester. Par exemple, un client HTTP, un client MySQL, etc.
On va vérifier s’il y a une bonne collaboration et comment se comporte l’implémentation quand le client retourne ses valeurs, etc.
Et pour nous faciliter la rédaction des tests, nous allons utiliser l'injection de dépendance, ce qui a pour autre effet de découpler le code.
https://gist.github.com/mickaelw/a0be671f605c28e051c44861a33ee863
On peut remarquer que les méthodes de l’HTTPClient sont doublées par une librairie de tests. Il est également possible de le faire manuellement.
L’adaptateur testé, HTTPBasketRepository :
https://gist.github.com/mickaelw/3f3ac8b565d8be40d8f71af0c414da12
HTTPBasketRepository est un adaptateur (une implémentation) réelle du port (contrat) BasketRepository.
Ici, nous vérifions les cas passant et non passant suivants :
À noter également, le jeu de mappers pour respecter les formats imposés par chaque partie.
Le but va être d'interagir réellement avec le tiers quand cela est possible.
À la différence du point précédent, on ne va pas se focaliser sur les interactions avec le client mais sur l’effet de bord qui résulte d’une action. C’est à dire, si on souhaite vérifier un ajout en base de données, on va vérifier qu’on récupère la bonne valeur.
https://gist.github.com/mickaelw/8eeb55c99186d8446c378545be16bd24
Nous pouvons remarquer un beforeEach et un afterEach avec respectivement, un ajout en base de données pour mettre le test dans un état propice, et la suppression de cette même donnée pour garder la base de données propre pour les tests suivants.
NDR : ici, on utilise l’ORM pour réaliser ces deux comportements. En effet, l’adaptateur qui est testé ne fait que récupérer les informations d’un client par rapport à son id.
https://gist.github.com/mickaelw/0a35d5b3a56c0f84c0a3beef97935615
SequelizeCustomerLoader est un adaptateur (une implémentation) réelle du port (contrat) CustomerLoader.
Ici, nous vérifions les cas passant et non passant suivants :
À noter, le jeu de mapper pour respecter le format imposé.
Pour ne pas s’embrouiller entre les tâches, on évite d’en faire plusieurs en même temps, pour nos tests c’est pareil.
Les TI doivent nous permettre de nous concentrer sur la bonne intégration avec un système tiers et chasser de notre esprit les règles métiers qui sont gérées par les tests unitaires et les tests d’acceptation.
L'objectif d’un TI est de tester l’intégration d’un système tiers (librairie, framework, etc.) pour être utilisé par nos Use Cases**.**
**Il va nous permettre de construire l’**Adaptateur par rapport au contrat défini par le port afférent et aux contraintes liées au système tiers choisi.
L’article n’a pas vocation à dire que l’une des façons de faire est mieux que l’autre mais à montrer les possibles et leurs implications, pour résumer :
Utilisation de test double :
Nous utilisons l’injection de dépendances dans la première façon de faire, afin de tester la collaboration entre le client et son utilisateur.
Avantages :
Inconvénients :
Utilisation de l'implémentation réelle :
L’injection de dépendances n’est pas nécessaire dans cette façon de faire, car nous regardons l’effet de bord.
Avantages :
Inconvénients :
Résumé en une image :