Comprendre le TDD : principes, architecture hexagonale et types de tests
Lors de l’AFUP Day 2024 à Nancy, organisé par l’Association Française des Utilisateurs de PHP, mes collègues et moi avons assisté à de nombreuses présentations. Dans cet article, j’avais envie de revenir sur la présentation de Jean-Pascal Langinier, un ancien Lead Développeur chez Adimeo, sur la thématique : « TDD : principe, architecture synergique, types et stratégie de tests ».
Je vous propose donc de repartir de cette conférence pour détailler ce qu’est le Test Driven Development (TDD), l’architecture hexagonale et évidemment je parlerai d’autres tests que vous pouvez réaliser dans vos projets digitaux.
Test Driven Development (TDD) : définition, avantages et exemple ?
Qu’est-ce qu’un TDD ?
Commençons par une définition. Un Test Driven Development (TDD), ou développement piloté par les tests, est une méthodologie de développement logiciel dans laquelle les tests automatisés sont écrits avant le code fonctionnel nécessaire pour passer ces tests. Ils favorisent ainsi un développement itératif et une architecture évolutive. Mais pour quels avantages ?
Quels sont les avantages des TDD ?
Cette méthode présente de nombreux avantages, mais pour éviter de tomber dans des généralités, je me suis concentrée sur les principaux avantages.
- Découverte du code depuis les intentions - Le TDD encourage les développeurs à comprendre d'abord les fonctionnalités requises avant de commencer à coder.
- Pas d'over-engineering - En se concentrant sur les tests nécessaires, les développeurs créent uniquement le code requis pour passer ces tests, ce qui évite ainsi des fonctionnalités superflues.
- Les tests servent de documentation - Les tests écrits au préalable servent de documentation vivante pour le code. La compréhension et la maintenance sont ainsi facilités.
- La sensation d'avancer tout le temps vers l'objectif - Le TDD offre un retour immédiat sur l'avancement grâce aux tests réussis. Les progrès réalisés et l’avancement du projet sont donc plus clairs.
- Plus d'attachement au code - En écrivant les tests avant le code, les développeurs sont moins susceptibles de s'attacher à leur production. En effet, ils savent que le code produit pourra être modifié ou remplacé… le principal étant que les tests passent.
- Tout est sécurisé en cas de régression - Les tests détectent rapidement les régressions et garantissent que les nouvelles modifications n'introduisent pas de bugs dans les fonctionnalités existantes.
Exemple de test TDD en PHP
Vous vous demandez à quoi ressemble ce type de test écrit en PHP ? Voici un exemple :
class InformAuctioneerTest extends KernelTestCase
{
private InMemoryTransport $transport;
public function setUp(): void
{
$this->bootKernel();
$this->transport = static::getContainer()->get('messenger.transport.sync');
}
public function testAuctioneerHasHisMessageInSfMessengerTransport(): void {
/** @var InformAuctioneer $sut */
$sut = static::getContainer()->get(InformAuctioneer::class);
// Création d'un produit non identifié pour tester
$product = new UnidentifiedProduct('a product', 14, 0);
// Appel de la méthode inform() de InformAuctioneer avec le produit créé
$sut->inform($product);
// Récupération du message envoyé via le transport Symfony Messenger
$message = $this->transport->get()[0]->getMessage();
// Assertion pour vérifier que le produit dans le message est identique au produit créé
$this->assertEquals($message->product(), clone $product);
}
}
Pour en savoir plus et découvrir d’autres exemples, je vous partage le GitHub de Jean-Pascal. 😀
Qu’est-ce que l’architecture hexagonale et comment facilite-t-elle la maintenance et la testabilité des applications ?
L'architecture hexagonale, également connue sous le nom de Ports et Adaptateurs, est un style architectural qui vise à rendre les applications plus modulaires et testables. Les principaux concepts de cette architecture se concentrent sur deux aspects : le côté UI et le côté dépendances.
L’architecture hexagonale côté UI
L'architecture hexagonale côté UI aide à séparer l'interface du cœur de l'application.
Dans ce modèle, l'interface utilisateur agit comme un adaptateur. Cela veut dire que l'UI permet aux utilisateurs de communiquer avec le cœur de l'application de manière indépendante. Cette communication se fait via des ports, qui sont des interfaces définies par le domaine métier.
Les ports sont des points d'entrée et de sortie fournis par le domaine métier. Ils permettent à l'interface utilisateur d'envoyer des commandes et de recevoir des réponses du cœur de l'application. Grâce à ces ports, l'UI ne dépend pas directement des détails internes du domaine, mais seulement des interfaces fournies par celui-ci.
L’architecture hexagonale côté dépendances
Les dépendances externes comme les bases de données, les services externes et les API sont aussi vues comme des adaptateurs dans l'architecture hexagonale. Cela signifie qu'elles interagissent avec le cœur de l'application via des ports ou interfaces, définis par le domaine métier.
Les bases de données, les services externes et les API sont des composants qui permettent au cœur de l'application de communiquer avec des ressources ou des services extérieurs. Dans l'architecture hexagonale, ces dépendances doivent suivre les ports définis par le domaine métier. Elles doivent donc respecter les règles et les interfaces que le domaine métier a mises en place.
Cette approche garantit que même si on change de base de données ou de service externe, le cœur de l'application reste intact et continue de fonctionner correctement. Les ports agissent comme une « couche intermédiaire », rendant le système plus flexible et facile à maintenir.
Poursuivez votre lecture avec cet article : « Implémentation d’une architecture hexagonale avec Symfony »
La pyramide de tests
La pyramide de tests est un concept clé pour structurer les tests dans une application. Elle a pour but de maximiser leur efficacité tout en minimisant les coûts de maintenance.
La pyramide est divisée en plusieurs niveaux :
- Tests unitaires - Ces tests sont à la base de la pyramide. Ils sont rapides, isolés et vérifient des parties spécifiques du code. Leur exécution rapide permet de détecter les problèmes dès les premières étapes du développement.
- Tests d'intégration – Situés au deuxième niveau de la pyramide, juste au-dessus des tests unitaires, ces tests vérifient que les différentes parties du système fonctionnent correctement ensemble. Ils sont plus lents et plus coûteux que les tests unitaires, mais essentiels pour assurer l'intégrité du système. Par exemple, on utilisera un test d’intégration pour tester la connexion à une base de données.
- Tests end-to-end (E2E) - Ces tests simulent des scénarios utilisateur complets. Ils sont les plus lents et les plus coûteux à mettre en place et à maintenir… mais ils fournissent une validation complète du système du point de vue de l'utilisateur. Cela comprend par exemple le teste d'un contrôleur ou d'une commande.
- Tests manuels – Enfin, les tests manuels sont au sommet de la pyramide. Ils sont parfois nécessaires pour valider des aspects non automatisables de l'application, comme l'expérience utilisateur.
Malgré toutes ces possibilités, la pyramide de tests ne suffit pas à elle-même. Elle ne répond pas à la question : « pourquoi on utilise tel type de test plutôt qu’un autre ? ». Elle oppose essentiellement la vitesse à la réalité du test.
Il est donc important de bien comprendre les objectifs et les limites de chaque niveau de test pour utiliser la pyramide de manière efficace dans un contexte de développement.
Les tests de mutation
Pour compléter notre pyramide des tests, les tests de mutation sont une technique qui consiste à introduire délibérément des modifications dans le code source pour vérifier si les tests existants détectent ces changements.
Les objectifs des tests de mutation sont simples :
- Améliorer la qualité des tests, en s'assurant qu’ils peuvent détecter des erreurs introduites volontairement. Ainsi, on garantit que les tests sont robustes et couvrent bien le code.
- Identifier les faiblesses des tests. Les mutations non détectées révèlent les zones du code qui ne sont pas correctement testées, ce qui permet d'améliorer la couverture des tests.
Ce qu’il faut retenir. L'adoption du TDD (Test Driven Development) et de l'architecture hexagonale, combinée avec une stratégie de tests structurée comme la pyramide de tests et des techniques avancées comme les tests de mutation, permet de créer des applications robustes, maintenables et testables.
En mettant l'accent sur la qualité dès le début du processus de développement, les entreprises peuvent réduire les coûts de maintenance, améliorer la satisfaction des utilisateurs et accroître la fiabilité de leurs logiciels et/ou applications.
De façon plus globale, vous voulez savoir comment mettre en œuvre le TDD et quel est son impact sur les projets ? Je vous propose de lire cet article : « Les avantages du Test Driven Development ».
Et pour ceux qui veulent lire nos articles issus de l’AFUP Day 2024 à Nancy, je vous partage la liste des articles publiés sur notre blog.
- Architecture à mettre en place pour un SaaS en fonction du client
- Maintien et scalabilité d'une application vieille de 20 ans
- Principes et implémentation d’OpenTelemetry dans des projets PHP
- Game-changing CSS : les techniques pour des sites Web performants
- Container, the hard way : un voyage à travers la complexité des conteneurs
- Introduction à l’Event Sourcing en PHP
Crédit photo : gorodenkoff