Principes de test
Références spécifiques au langage
Pour les schémas de test spécifiques au langage, lisez également :
- TypeScript (Vitest/RTL/Playwright) : references/typescript.md
Philosophie fondamentale des tests
- Les tests sont du code de première classe : Maintenez une qualité de test égale à celle du code de production
- Retour rapide : Les tests doivent s'exécuter rapidement et fournir un retour immédiat
- Fiabilité : Les tests doivent être déterministes et reproductibles
- Indépendance : Chaque test doit s'exécuter en isolation
Processus TDD [OBLIGATOIRE pour toute modification de code]
Exécutez ce processus pour chaque modification de code :
Phase ROUGE
ÉTAPE 1 : Écrire un test qui définit le comportement attendu ÉTAPE 2 : Exécuter le test ÉTAPE 3 : Confirmer que le test ÉCHOUE (s'il réussit, le test est erroné)
Phase VERTE
ÉTAPE 1 : Écrire le code MINIMAL pour faire passer le test ÉTAPE 2 : Exécuter le test ÉTAPE 3 : Confirmer que le test RÉUSSIT
Phase REFACTOR
ÉTAPE 1 : Améliorer la qualité du code (éliminer les duplications, améliorer le nommage) ÉTAPE 2 : Exécuter le test ÉTAPE 3 : Confirmer que le test RÉUSSIT TOUJOURS
Phase VÉRIFIER [OBLIGATOIRE - 0 ERREURS REQUISES]
ÉTAPE 1 : Exécuter TOUTES les commandes de contrôle qualité pour votre langage/projet ÉTAPE 2 : Corriger toute erreur jusqu'à ce que TOUTES les commandes réussissent avec 0 erreur ÉTAPE 3 : Confirmer l'absence de régressions
APPLICATION : Impossible de passer à la phase suivante en cas d'échec d'un contrôle qualité
Exceptions TDD (pas de TDD requis)
- Fichiers de configuration purs
- Documentation uniquement
- Correctifs d'urgence (mais ajoutez des tests immédiatement après)
- Explorations ponctuelles (jetez ou réécrivez avec des tests avant fusion)
- Scripts de construction/déploiement (sauf s'ils contiennent de la logique métier)
Exigences de qualité [OBLIGATOIRES]
Couverture
- Traitez la couverture comme un signal de diagnostic pour trouver les zones non testées, pas comme un objectif. Les objectifs sont détournés en tests triviaux.
- Concentrez les tests sur les chemins critiques, la logique métier et les comportements dont la régression aurait de l'importance.
- Priorisez les assertions significatives plutôt que le nombre de couverture. Tout seuil requis provient du CI du projet, du fichier de tâche, du plan de travail ou du Design Doc.
Caractéristiques des tests
Tous les tests DOIVENT être :
- Indépendants : Aucune dépendance entre les tests
- Reproductibles : La même entrée produit toujours la même sortie
- Rapides : Terminer la suite de tests complète dans la fenêtre de retour acceptable du projet et signaler les suites qui ralentissent matériellement l'itération locale ou le CI
- Autovérifiants : Échec/réussite clair sans vérification manuelle
- Opportuns : Écrits près du code qu'ils testent
APPLICATION : Les tests ne répondant à AUCUNE caractéristique DOIVENT être corrigés immédiatement
Types de tests
Tests unitaires
Objectif : Tester des composants individuels en isolation
Caractéristiques :
- Test d'une seule fonction, méthode ou classe
- Exécution rapide (millisecondes)
- Aucune dépendance externe
- Simuler les services externes
- Majorité de votre suite de tests
Tests d'intégration
Objectif : Tester les interactions entre composants
Caractéristiques :
- Test de plusieurs composants ensemble
- Peut inclure base de données, système de fichiers ou API
- Plus lents que les tests unitaires
- Vérifier les contrats entre modules
- Partie plus petite de la suite de tests
Tests de bout en bout (E2E)
Objectif : Tester des flux de travail complets du point de vue de l'utilisateur
Caractéristiques :
- Test de la pile applicative entière
- Simuler des interactions utilisateur réelles
- Type de test le plus lent
- Moins nombreux
- Niveau de confiance le plus élevé
Pyramide des tests
Suivez la structure de la pyramide des tests :
/\ <- Quelques tests E2E (Haute confiance, lents)
/ \
/ \ <- Certains tests d'intégration (Confiance moyenne, vitesse moyenne)
/ \
/________\ <- Beaucoup de tests unitaires (Rapides, fondamentaux)
Principes de conception des tests
Pattern AAA (Arrange-Act-Assert)
Structurez chaque test en trois phases claires :
// Arrange : Configurer les données et conditions de test
user = createTestUser()
validator = createValidator()
// Act : Exécuter le code sous test
result = validator.validate(user)
// Assert : Vérifier le résultat attendu
assert(result.isValid == true)
Une assertion par concept
- Tester un comportement par cas de test
- Plusieurs assertions OK si elles testent un concept unique
- Scinder les assertions non liées dans des tests séparés — un test DOIT vérifier un seul comportement
Noms de tests descriptifs
Les noms de tests doivent clairement décrire :
- Ce qui est testé
- Sous quelles conditions
- Quel est le résultat attendu
Format recommandé : "doit [comportement attendu] lorsque [condition]"
Indépendance des tests
Exigences d'isolation
- Chaque test crée ses propres données de test
- Aucune dépendance vis-à-vis de l'ordre d'exécution
- Nettoyer son propre état
- Réussir lorsqu'il est exécuté en isolation
Configuration et nettoyage
- Utiliser des hooks de configuration pour préparer l'environnement de test
- Utiliser des hooks de nettoyage pour libérer les ressources
- Limiter la configuration aux données, dépendances et fixtures nécessaires au comportement testé
- S'assurer que le nettoyage s'exécute même si le test échoue
Mocking et doublures de test
Quand utiliser des mocks
- Simuler les dépendances externes : API, bases de données, systèmes de fichiers
- Simuler les opérations lentes : Appels réseau, calculs lourds
- Simuler les comportements imprévisibles : Valeurs aléatoires, heure actuelle
- Simuler les services indisponibles : Services tiers
Principes de mocking [OBLIGATOIRES]
- Mocker aux limites, pas en interne — utiliser des implémentations réelles pour les utilitaires internes
- Limiter chaque mock au comportement que le test doit contrôler ou observer
- Vérifier les attentes du mock lorsque c'est pertinent
- Utiliser des adaptateurs pour les bibliothèques/cadres externes que vous ne contrôlez pas
Types de doublures de test
- Stub : Renvoie des valeurs prédéterminées
- Mock : Vérifie qu'il a été appelé correctement
- Spy : Enregistre des informations sur les appels
- Fake : Implémentation simplifiée fonctionnelle
- Dummy : Passé mais jamais utilisé
Tests de la couche de données
Limites des mocks pour l'accès aux données
Les mocks valident les modèles d'appel mais ne valident pas l'exactitude du schéma, la correction des requêtes ou les contraintes de stockage. Exemples de problèmes que les mocks peuvent manquer :
- dérive de schéma
- discordances de colonnes ou de champs
- jointures, filtres ou agrégations incorrects
- incompatibilité de migration
Quand la vérification de la couche de données réelle ajoute de la valeur
Utilisez une vérification d'accès aux données réelle ou proche de la production lors du test :
- des implémentations de dépôt ou DAO
- des mappings ORM
- des constructeurs de requêtes ou SQL brut
- du comportement de persistance qui dépend de contraintes ou de la forme du schéma
Options d'environnement
Choisissez l'option la plus pratique pour l'environnement du projet :
- base de données conteneurisée
- base de données de test dédiée
- base de données en mémoire avec limitations documentées
- banc d'essai local basé sur un adaptateur
Alignement de conception
Lorsqu'un Design Doc inclut des limites de test, suivez-le comme base pour décider quelles dépendances restent réelles et quelles limites sont isolées.
Pratiques de qualité des tests [OBLIGATOIRES]
Garder les tests actifs
- Corriger ou supprimer les tests qui échouent : Résoudre les échecs immédiatement
- Supprimer les tests commentés : Les corriger ou les supprimer entièrement
- Garder les tests en cours d'exécution : Les tests cassés perdent rapidement de la valeur
- Maintenir la suite de tests : Refactoriser les tests si nécessaire
Qualité du code de test
- Appliquer les mêmes normes que le code de production
- Utiliser des noms de variables descriptifs
- Extraire des helpers de test pour réduire la duplication
- Garder les tests lisibles et maintenables
Helpers et utilitaires de test
- Créer des constructeurs de données de test réutilisables
- Extraire la configuration commune dans des fonctions helpers
- Construire des utilitaires de test pour les scénarios complexes
- Partager les helpers entre les fichiers de test de manière appropriée
Que tester
Se concentrer sur le comportement
Tester le comportement observable, pas l'implémentation :
- Bien : Tester que la fonction renvoie la sortie attendue
- Bien : Tester que le bon point d'API est appelé
- Mal : Tester qu'une variable interne a été définie
- Mal : Tester l'ordre des appels de méthodes privées
Tester les cas limites
Toujours tester :
- Conditions limites : Valeurs min/max, collections vides
- Cas d'erreur : Entrée invalide, valeurs nulles, données manquantes
- Cas limites : Caractères spéciaux, valeurs extrêmes
- Chemin heureux : Utilisation normale et attendue
Critères de qualité des tests [OBLIGATOIRES]
- Attentes littérales : Utiliser des valeurs littérales codées en dur dans les assertions — la valeur attendue ≠ la valeur de retour du mock (l'implémentation traite les données)
- Vérification des résultats : Assert sur les valeurs de retour et l'état, pas l'ordre des appels
- Assertions significatives : Chaque test DOIT avoir au moins une assertion — un test sans assertion n'apporte aucune valeur
- Mocker uniquement les E/S externes : Mocker DB/API/système de fichiers, utiliser les utilitaires internes réels
- Couverture des limites : Inclure les cas vides/zéro/max/erreur avec les chemins heureux
APPLICATION : Les tests violant UN QUELCONQUE critère DOIVENT être réécrits
Exigences de vérification [OBLIGATOIRES pour la phase VÉRIFIER]
Liste de vérification avant validation
☐ Tous les tests réussissent ☐ Aucun test ignoré ou commenté ☐ Aucun code de débogage laissé dans les tests ☐ Le seuil de couverture est respecté lorsque le projet, le fichier de tâche, le plan de travail ou le Design Doc en définit un ☐ Les tests s'exécutent en un temps raisonnable
Politique de tolérance zéro
- Zéro test échoué : Corriger immédiatement
- Zéro test ignoré : Supprimer ou corriger
- Zéro test instable : Rendre déterministe
- Zéro test lent : Optimiser ou scinder
APPLICATION : Impossible de poursuivre l'achèvement de la tâche si UN QUELCONQUE contrôle qualité échoue
Organisation des tests
Structure des fichiers
- Miroir de la structure de production : Les tests suivent l'organisation du code
- Conventions de nommage claires : Suivre les modèles de fichiers de test du projet
- Regroupement logique : Grouper les tests associés ensemble
- Séparation des types de tests : Tests unitaires, d'intégration, E2E dans des répertoires séparés
Organisation de la suite de tests
tests/
├── unit/ # Tests unitaires rapides et isolés
├── integration/ # Tests d'intégration
├── e2e/ # Tests de bout en bout
├── fixtures/ # Données de test et fixtures
└── helpers/ # Utilitaires de test partagés
Considérations de performance
Vitesse des tests
- Tests unitaires : < 100 ms chacun
- Tests d'intégration : < 1 s chacun
- Suite complète : Doit s'exécuter fréquemment (< 10 minutes)
Anti-patrons courants
Détecter et éliminer ces patrons immédiatement :
- Tests qui ne testent rien (réussissent toujours)
- Tests qui dépendent de l'ordre d'exécution
- Tests qui dépendent d'un état externe
- Tests avec une logique complexe (tests qui ont besoin de leurs propres tests)
- Tester les détails d'implémentation au lieu du comportement observable
- Mocking excessif (mocker uniquement les limites, utiliser les internes réels)
- Duplication de code de test
Tests instables
Éliminer les tests qui échouent de manière intermittente :
- Supprimer les dépendances temporelles
- Utiliser des données déterministes au lieu de valeurs aléatoires
- Assurer un nettoyage approprié
- Corriger les conditions de concurrence
- Rendre tous les tests déterministes
Tests de régression
- Ajouter un test pour chaque correction de bogue
- Maintenir une suite de tests complète
- Exécuter la suite complète régulièrement
- Supprimer un test uniquement lorsque le comportement couvert n'existe plus ou que le même comportement est couvert par un test plus solide au niveau approprié
Code hérité
- Ajouter des tests de caractérisation avant le refactoring
- Tester d'abord le comportement existant
- Améliorer progressivement la couverture
- Refactoriser en toute confiance