Chapitre 216 minIntermédiaire
Les types de tests et la pyramide des tests
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- distinguer test unitaire, d'intégration, de bout en bout et d'acceptation ;
- classer un test : fonctionnel ou non, boîte noire ou boîte blanche ;
- doser les tests avec la pyramide et éviter le cornet de glace ;
- placer chaque niveau de test dans le cycle en V et en agile.
4 exercices corrigés2 schémas
On classe les tests selon trois questions : quelle taille de logiciel ils vérifient (une fonction, plusieurs composants, l'application entière), quoi ils vérifient (une fonctionnalité, la performance, la sécurité) et qui les écrit (le développeur, l'équipe, l'utilisateur). Les tests unitaires, d'intégration, de bout en bout, de non-régression et d'acceptation ne sont pas concurrents : chacun attrape des défauts que les autres laissent passer. La pyramide des tests dit dans quelles proportions les combiner.
Les niveaux de test : du plus petit au plus grand
| Niveau | Ce qu'il vérifie | Qui l'écrit | Exemple dans Prêt'Matos |
|---|---|---|---|
| Test unitaire | Une unité de code isolée : une fonction, une méthode, une classe | Le développeur | verifierReservation refuse un prêt de 16 jours |
| Test d'intégration | Plusieurs composants qui travaillent ensemble : une route, ses middlewares, la base | Le développeur, l'équipe | POST /api/reservations enregistre la réservation en base et renvoie 201 |
| Test système (ou de bout en bout) | L'application entière, déployée, du point de vue de l'utilisateur | L'équipe, les testeurs | Julie se connecte, réserve VP-02, voit la confirmation à l'écran |
| Test d'acceptation | Le logiciel répond-il au besoin ? | Le client, les utilisateurs, avec l'équipe | Nadia déroule le cahier de recette et signe le procès-verbal |
Le test unitaire
Un test unitaire vérifie le comportement d'une petite unité de code, isolée du reste : il ne touche ni la base de données, ni le réseau, ni le disque, ni l'horloge réelle. Il s'exécute en quelques millisecondes et, quand il échoue, il désigne une fonction précise. C'est le test que tu écriras le plus souvent ; les chapitres 3 à 6 lui sont consacrés.
test('refuse un prêt de 16 jours', () => {
const demande = { materiel: 'VP-02', debut: '2027-03-15', fin: '2027-03-30' };
expect(verifierReservation(demande, [], '2027-03-08').motif).toBe('Un prêt dure au plus 15 jours.');
});
Le test d'intégration
Un test d'intégration vérifie que des composants qui marchent séparément marchent aussi ensemble. Les défauts d'intégration sont nombreux et sournois : une route qui oublie le middleware d'authentification, une requête SQL qui renvoie les dates dans un autre format que celui attendu par le code, un nom de champ JSON différent entre l'API et le front. Chaque morceau a ses tests unitaires verts, et pourtant l'ensemble ne marche pas. Le chapitre 8 montre comment tester une API Express avec Supertest.
test('renvoie 409 quand le matériel est déjà réservé', async () => {
const reponse = await request(app)
.post('/api/reservations')
.set('Authorization', 'Bearer jeton-julie')
.send({ materiel: 'PC-07', debut: '2027-03-12', fin: '2027-03-18' });
expect(reponse.status).toBe(409);
});
Le test de bout en bout
Un test de bout en bout (end-to-end, E2E) pilote l'application entière comme le ferait un utilisateur : un vrai navigateur ouvre la page, remplit le formulaire, clique sur le bouton et vérifie ce qui s'affiche. Il traverse toutes les couches : front React, API, base de données. C'est le test le plus réaliste, mais aussi le plus lent (plusieurs secondes), le plus fragile (un libellé de bouton change et il casse) et le plus difficile à diagnostiquer (quand il échoue, le défaut peut être n'importe où). Le chapitre 9 l'écrit avec Playwright.
Fonctionnel ou non fonctionnel
Les niveaux disent à quelle échelle on teste. Les types disent quoi :
| Type | Question posée | Exemples |
|---|---|---|
| Test fonctionnel | Le logiciel fait-il ce qu'il doit faire ? | Une réservation valide est enregistrée ; un chevauchement est refusé |
| Test de performance | Est-il assez rapide, sous la charge prévue ? | La liste des matériels répond en moins de 300 ms avec 20 utilisateurs simultanés |
| Test de sécurité | Résiste-t-il aux usages malveillants ? | Un salarié ne peut pas lire les réservations des autres en changeant l'identifiant dans l'URL |
| Test d'utilisabilité | Est-il facile à utiliser ? | Un nouveau salarié réserve un portable sans aide en moins de deux minutes |
| Test d'accessibilité | Est-il utilisable par tous, y compris au clavier ou avec un lecteur d'écran ? | Chaque champ du formulaire a une étiquette lue par le lecteur d'écran |
| Test de compatibilité | Fonctionne-t-il sur les navigateurs et appareils prévus ? | Firefox, Chrome, Safari, en largeur téléphone |
Le mot « test fonctionnel » désigne aussi, dans beaucoup d'équipes, un test qui vérifie une fonctionnalité du point de vue de l'utilisateur, sans regarder le code : il se situe alors entre le test d'intégration et le test de bout en bout. Ne t'arrête pas aux étiquettes, qui varient d'une entreprise à l'autre : demande-toi ce que le test vérifie et à quelle échelle. Les tests de performance et de sécurité ont leur chapitre 13.
Le test de non-régression
Une régression est une fonctionnalité qui marchait et qui ne marche plus après une modification. Le test de non-régression consiste à rejouer, après chaque modification, les tests qui passaient avant, pour vérifier que rien n'a été cassé. Ce n'est pas un niveau de plus : ce sont tes tests unitaires, d'intégration et de bout en bout, relancés systématiquement. On ajoute aussi, après chaque bug corrigé, le test qui l'aurait détecté. Le chapitre 11 donne la méthode.
Le test d'acceptation et la recette
Le test d'acceptation vérifie que le logiciel répond au besoin exprimé, et pas seulement qu'il fonctionne. En France, on parle de recette : le client (la maîtrise d'ouvrage, MOA) ou le Product Owner déroule un cahier de recette, des cas de test tirés des exigences ou des critères d'acceptation des user stories, puis prononce la réception dans un procès-verbal. En anglais, on parle d'user acceptance testing (UAT). Deux variantes existent pour les logiciels diffusés largement : le test alpha, par des utilisateurs dans les locaux de l'éditeur, et le test bêta, par des utilisateurs réels chez eux, sur une version presque finie. Le chapitre 14 montre comment rédiger un plan de test et un cahier de recette.
Le test de fumée
Un test de fumée (smoke test) est une vérification très rapide, juste après un déploiement, que l'essentiel fonctionne : l'application démarre, la page d'accueil répond, la connexion à la base marche. L'expression vient de l'électronique : on allume l'appareil et on vérifie qu'il ne fume pas. S'il échoue, inutile de lancer les autres tests : on revient à la version précédente.
# Test de fumée de la recette de Prêt'Matos : l'API répond-elle ?
curl --fail --silent https://recette.pretmatos.example/api/sante
Boîte noire et boîte blanche
| Approche | Le testeur connaît… | Il choisit ses cas à partir de… | Exemple |
|---|---|---|---|
| Boîte noire | Les entrées, les sorties et les règles, pas le code | La spécification, les critères d'acceptation | Tester les durées 15 et 16 jours parce que la règle dit « au plus 15 jours » |
| Boîte blanche | Le code source | Les chemins du code : chaque if, chaque boucle | Écrire un test pour chacun des quatre return de verifierReservation |
| Boîte grise | Un peu des deux | La spécification et la structure (la base, l'API) | Tester l'API en connaissant le schéma de la base |
Les deux approches se complètent : la boîte noire trouve les cas que le développeur n'a pas codés (une règle oubliée), la boîte blanche trouve les branches du code qu'aucun test n'exécute. La couverture de code (chapitre 12) mesure ce second point.
Test statique et test dynamique
Tous les tests vus jusqu'ici exécutent le code : ce sont des tests dynamiques. Les tests statiques examinent le code ou les documents sans les exécuter :
- la revue de code, quand un collègue relit ta pull request (Git et GitHub) ;
- l'analyse statique : un linter comme ESLint, le vérificateur de types de TypeScript ou du compilateur C#, un outil d'analyse de sécurité (SAST) ;
- la relecture d'une spécification ou d'une user story, qui trouve les ambiguïtés avant même d'écrire le code.
Un test statique est souvent le moins cher de tous : ESLint signale une variable inutilisée ou un await oublié avant que tu lances quoi que ce soit.
La pyramide des tests
Combien écrire de tests de chaque niveau ? La pyramide des tests, popularisée par Mike Cohn dans les années 2000, répond par une image : une large base de tests unitaires, une couche plus fine de tests d'intégration, une pointe de tests de bout en bout.
Pourquoi cette forme ? Parce que les tests du bas sont rapides, stables et précis : mille tests unitaires tournent en une seconde, et celui qui échoue désigne la fonction fautive. Les tests du haut sont réalistes mais lents et fragiles : un test de bout en bout qui échoue peut venir du front, de l'API, de la base ou d'un réseau lent. On vérifie donc chaque règle métier en détail en bas, et on garde le haut pour quelques parcours essentiels, comme « réserver un matériel » et « enregistrer une sortie ».
Pour les applications front-end, Kent C. Dodds a proposé en 2018 une variante, le trophée des tests : une base d'analyse statique (ESLint, TypeScript), peu de tests unitaires, beaucoup de tests d'intégration de composants, et quelques tests de bout en bout. L'idée est la même : investir là où le rapport confiance / coût est le meilleur pour ton type d'application. Retiens la règle commune : chaque règle se teste au niveau le plus bas possible, et les niveaux du haut vérifient que les morceaux s'assemblent.
Les tests dans le cycle de vie du projet
Dans un projet mené en cycle en V (gestion de projet), chaque étape de conception, sur la branche descendante, a sa phase de test sur la branche montante. Les tests sont préparés au moment de la conception, même s'ils sont exécutés plus tard.
En agile (Scrum), il n'y a pas de grande phase de test à la fin : chaque user story est testée dans le sprint où elle est développée. La Definition of Done de l'équipe exige en général que les tests automatisés soient écrits et passent, et le Product Owner vérifie les critères d'acceptation de chaque story (user stories et critères d'acceptation). On parle de shift left : déplacer les tests vers la gauche du calendrier, le plus tôt possible.
Qui teste quoi ?
| Acteur | Ce qu'il teste | Avec quoi |
|---|---|---|
| Le développeur | Son code, au fur et à mesure | Tests unitaires et d'intégration, sur son poste puis dans la CI |
| L'équipe de développement (MOE) | L'application assemblée | Tests de bout en bout, tests de non-régression, tests de performance |
| Le relecteur | Le code d'un collègue | Revue de pull request |
| Le Product Owner ou la MOA | La conformité au besoin | Critères d'acceptation, cahier de recette |
| Les utilisateurs clés | L'usage réel | Recette, tests bêta |
| La sécurité ou un auditeur externe | La résistance aux attaques | Revue de code, SAST, DAST, test d'intrusion autorisé |
Exercices
Exercice 1 sur 4 Quel niveau de test ? Réussi À revoir
Pour chaque test de Prêt'Matos, donne son niveau (unitaire, intégration, bout en bout, acceptation) et, s'il y a lieu, son type non fonctionnel.
- Vérifier que
joursDeRetard('2027-03-14', '2027-03-17')renvoie 3. - Vérifier que
GET /api/reservations/messans jeton renvoie 401. - Nadia réserve un portable sur le serveur de recette et confirme que l'écran correspond à ce qu'elle a demandé.
- Un robot ouvre Chrome, se connecte avec le compte de Julie, réserve VP-02 et vérifie le message affiché.
- Vérifier que l'API répond en moins de 300 ms à 20 utilisateurs simultanés.
- Vérifier que la requête du dépôt MySQL renvoie les dates au format
AAAA-MM-JJ.
Voir la réponseMasquer la réponse de l'exercice 1
- Unitaire : une fonction pure, isolée.
- Intégration : la route et son middleware d'authentification ensemble. C'est aussi un test de sécurité (contrôle d'accès).
- Acceptation (recette) : l'utilisatrice valide la conformité au besoin.
- Bout en bout : toute l'application, pilotée par un navigateur.
- Test non fonctionnel de performance (test de charge), au niveau système.
- Intégration : le code et la vraie base de données ensemble. Un test unitaire avec un dépôt en mémoire ne verrait pas ce défaut.
Tu avais trouvé ?
Exercice 2 sur 4 Diagnostiquer une suite de tests Réussi À revoir
L'équipe d'un projet de gestion de stages a 4 tests unitaires, 6 tests d'intégration et 85 tests Selenium qui remplissent les formulaires dans un navigateur. La suite complète dure 52 minutes et, deux fois sur trois, un ou deux tests échouent sans que le code ait changé.
- Comment s'appelle cette forme de suite ?
- Pourquoi est-elle lente et instable ?
- Propose trois actions.
Voir la réponseMasquer la réponse de l'exercice 2
- Un cornet de glace : la pyramide à l'envers.
- Chaque test de bout en bout démarre un navigateur et traverse toutes les couches : il dure plusieurs secondes et dépend du temps de chargement, du réseau, de l'état de la base. Plus il y en a, plus la probabilité qu'au moins un échoue au hasard augmente.
- Par exemple : réécrire en tests unitaires les vérifications de règles métier (calculs, validations de dates) aujourd'hui faites par formulaire ; tester les routes de l'API avec des tests d'intégration ; ne garder que quelques tests de bout en bout pour les parcours essentiels (se connecter, déposer une convention, la valider) ; repérer et corriger chaque test instable au lieu de relancer la suite (les tests instables).
Tu avais trouvé ?
Exercice 3 sur 4 Boîte noire ou boîte blanche ? Réussi À revoir
- Une testeuse lit la règle « le rappel part la veille de la date de retour » et prépare un prêt qui se termine demain, un qui se termine aujourd'hui, un qui se termine après-demain. Quelle approche ?
- Un développeur lit le rapport de couverture, voit que le bloc
catchdeenvoyerRappelsn'est jamais exécuté et écrit un test où l'envoi échoue. Quelle approche ? - Laquelle des deux aurait pu trouver une règle oubliée dans le code ?
Voir la réponseMasquer la réponse de l'exercice 3
- Boîte noire : les cas viennent de la règle, pas du code.
- Boîte blanche : le cas vient de la structure du code (une branche non couverte).
- La boîte noire : si le code ne contient pas la règle, aucune branche ne la représente, et la boîte blanche ne peut pas voir ce qui n'existe pas. C'est la limite principale de la couverture de code.
Tu avais trouvé ?
Exercice 4 sur 4 Placer les tests dans le cycle en V Réussi À revoir
Associe chaque document de conception de Prêt'Matos au niveau de test qu'il sert à préparer.
- Le cahier des charges, avec l'exigence « un salarié réserve lui-même le matériel ».
- La spécification de la route
POST /api/reservationset de ses codes de retour. - Le schéma d'architecture : front React, API Express, base MySQL.
- Le détail de l'algorithme de
periodesSeChevauchent.
Voir la réponseMasquer la réponse de l'exercice 4
- Recette (tests d'acceptation) : on vérifiera avec la MOA que le besoin est couvert.
- Tests de validation (système, fonctionnels) : chaque code de retour spécifié devient un cas de test.
- Tests d'intégration : vérifier que le front parle bien à l'API, et l'API à la base.
- Tests unitaires : chaque cas de l'algorithme (chevauchement partiel, englobant, périodes contiguës) devient un test.
Tu avais trouvé ?