Après avoir vu pourquoi et comment viser la qualité dans la conception logicielle, penchons-nous à présent sur la testabilité et la structure du code. L’objectif : disposer de tests fiables, rapides, et directement alignés sur les règles métier. Dans cette optique, nous aborderons la question du découplage, de l’inversion de dépendances et de la classification des tests (unitaires, intégration, end-to-end).
Les tests manuels, souvent perçus comme un mal nécessaire, finissent par peser lourd sur les équipes :
La solution réside dans l’automatisation. Ainsi, chaque nouveau développement est accompagné de tests qui peuvent être rejoués à l’infini pour détecter toute régression.
Il est utile de distinguer deux grandes catégories de fonctionnalités :
Pour tester efficacement, il faut comprendre ce que l’on vérifie :
En software craftsmanship, on recommande une architecture qui facilite les tests :
Le test unitaire couvre un scénario métier précis, souvent articulé autour d’une commande (action) ou d’une query. Il suit en général la structure AAA :
L’intérêt d’un test unitaire est qu’il reste rapide, isolé et fiable. Les retours négatifs ou positifs sont immédiats, ce qui encourage la pratique du TDD (Test-Driven Development) ou, au minimum, l’écriture de tests systématiques.
Un test unitaire efficace suit l’acronyme F.I.R.S.T. :
On appelle “test doubles” l’ensemble des objets qui remplacent des composants réels lors de l’exécution des tests. Parmi eux :
L’objectif est de contrôler l’environnement pour éviter toute variation ou lenteur. Par exemple, si votre code appelle un API externe, remplacez-la par un stub renvoyant la réponse désirée ; ainsi, votre test reste rapide et déterministe.
Pour garder des tests lisibles, on utilise souvent :
createEvent("titre", "2025-03-15")
pour générer des objets courants.L’essentiel est de simplifier la création d’objets dans vos tests, afin de se concentrer sur le scénario et la lisibilité des intentions.
La règle d’or est de ne pas abuser des tests E2E, qui sont longs et fragiles. Miser sur une majorité de tests unitaires (rapides, simples à maintenir) complétés par quelques tests d’intégration et E2E ciblés pour valider les chemins critiques.
L’architecture hexagonale, dite Ports & Adapters, propose de séparer :
Cette disposition rend le cœur aisément testable. Si vous avez besoin d’intégrer un nouvel outil ou un nouveau service, vous créez un adaptateur, sans toucher au noyau. En test, il est ainsi simple de remplacer un adaptateur par un fake, un stub ou un mock.
En optant pour une architecture modulaire et un usage massif de tests automatisés, l’équipe gagne en sérénité et en productivité. Chaque nouvelle fonctionnalité est validée presque instantanément, et les régressions sont détectées tôt. Les développeurs peuvent évoluer rapidement, sachant que leur pipeline de tests veille à la cohérence du système.
Le software craftsmanship trouve ici sa concrétisation : produire un code de haute qualité, maintenable, au service d’une livraison continue de valeur. En s’appuyant sur des pratiques éprouvées – TDD, inversion de dépendances, tests unitaires – on parvient à donner vie à un écosystème logiciel capable de s’adapter aux changements, sans rompre sous la complexité ou la dette technique.
Ainsi se ferme la boucle initiée en Partie 1 : de la philosophie de l’artisanat logiciel à la pratique quotidienne des tests et de l’architecture hexagonale, le software craftsmanship nous encourage à viser l’excellence et à partager nos connaissances, pour le bénéfice de toute l’équipe et de nos utilisateurs finaux.