Les tests automatisés
Comprendre la pyramide des tests, écrire des tests unitaires lisibles, faire du TDD, automatiser l'intégration et le end-to-end. Pour pouvoir refactorer sans angoisse.
À la fin du cours, tu sais
- Expliquer ce qu'un test fait gagner (régression, refacto, doc vivante)
- Écrire un test unitaire propre selon le pattern AAA
- Pratiquer le TDD (red → green → refactor)
- Distinguer test unitaire, d'intégration et e2e
- Mettre en place des tests Playwright sur les parcours critiques
- Brancher les tests en CI sans gaspiller des minutes
Prérequis
- Maîtriser JavaScript ou TypeScript
- Avoir un projet sur lequel pratiquer (ou en créer un minimal pour le cours)
Étape 1 sur 11 : Pourquoi tester ton code
Chapitre 1
Pourquoi tester ton code
Un test, c'est un filet de sécurité qui te laisse refactorer sans angoisse. Sans tests, chaque modification est une roulette russe.
Beaucoup de débutant·es voient les tests comme une corvée qui ralentit le « vrai » travail. C'est une erreur de perspective : un test est un filet de sécurité qui te permet d'aller plus vite, pas moins. Sans tests, chaque modification est une roulette russe — tu changes une fonction et tu pries pour ne rien avoir cassé ailleurs. Avec des tests, tu sais en quelques secondes si ton changement respecte le comportement attendu. Le temps passé à écrire un test est un investissement qui se rembourse au premier bug évité.
Les bénéfices sont concrets et se cumulent. L'anti-régression d'abord : le test t'alerte immédiatement si tu casses l'existant. La refacto sereine ensuite : tu peux réorganiser ton code sans changer son comportement, les tests verts te le confirment. La documentation vivante aussi : un test bien nommé décrit l'intention métier mieux qu'un commentaire, et il ne périme jamais puisqu'il s'exécute. Et enfin la confiance : pouvoir livrer un vendredi soir sans la boule au ventre, parce qu'un ensemble de tests veille à ta place.
Il existe un bénéfice moins évident mais précieux : le test est le premier utilisateur de ton code. Pour écrire un test, tu dois appeler ta fonction depuis l'extérieur, comme le fera le reste de l'application. Tu réalises alors très vite si ton interface est agréable ou pénible à utiliser. Un code difficile à tester est presque toujours un code mal découpé — trop de responsabilités, trop de dépendances cachées. Écrire les tests devient ainsi un signal de conception : la difficulté à tester te pousse, en retour, vers un meilleur design.
- Anti-régression : tu modifies une fonction, le test te dit immédiatement si tu casses l'existant
- Refacto sereine : tu changes l'implémentation sans changer le comportement, les tests verts valident
- Documentation vivante : un test bien nommé décrit l'intention métier mieux qu'un commentaire
- Confiance en prod : merger un vendredi soir sans avoir la boule au ventre
- Feedback de design : un code difficile à tester est souvent un code mal découpé
Le test comme premier·e utilisateur·ice de ton code
Chapitre 2
La pyramide des tests
Beaucoup de tests rapides à la base, peu de tests lents au sommet. Inverser la pyramide ralentit la CI et rend les tests fragiles.
Tous les tests ne se valent pas, et savoir doser les différents types est une compétence clé. La pyramide des tests est le modèle mental qui guide ce dosage : beaucoup de tests rapides à la base, peu de tests lents au sommet. On distingue trois niveaux. Les tests unitaires (environ 70 %) vérifient une fonction ou une classe isolée, s'exécutent en quelques millisecondes. Les tests d'intégration (environ 20 %) vérifient que plusieurs briques collaborent correctement — le code et la base, deux modules entre eux. Les tests end-to-end (environ 10 %) simulent un·e utilisateur·ice à travers toute l'application.
Pourquoi cette forme de pyramide plutôt qu'une autre ? Parce que la vitesse et la stabilité décroissent à mesure qu'on monte. Un test unitaire est rapide, déterministe et pointe précisément la ligne fautive. Un test end-to-end est lent, sensible au moindre changement d'interface, et quand il échoue il te dit « quelque chose ne va pas » sans dire où. Une suite avec beaucoup d'unitaires reste rapide et fiable ; une suite dominée par les e2e devient lente et fragile.
L'anti-modèle à éviter porte un nom imagé : le cornet de glace — la pyramide inversée, avec une majorité de tests e2e. Le résultat est prévisible : une suite qui met trente minutes, échoue au hasard, et que plus personne n'ose lancer. La règle d'or pour choisir le bon niveau : si un test unitaire suffit, n'écris pas d'intégration ; si l'intégration suffit, n'écris pas d'e2e. Concrètement, une fonction de calcul (une TVA, un total de panier) appelle un test unitaire ; une couche d'accès aux données, un test d'intégration ; un parcours critique comme le paiement, un test e2e. Chaque outil à sa place.
- Unitaires (≈ 70 %) : rapides (ms), isolés, testent une fonction pure ou une classe
- Intégration (≈ 20 %) : vérifient que plusieurs modules collaborent (DB, API interne)
- End-to-end (≈ 10 %) : simulent un·e utilisateur·ice à travers toute la stack
L'anti-pattern du cornet de glace
Quel test pour quel cas
- Une fonction de calcul (ex : TVA, panier) → unitaire
- Une couche d'accès à une base ou un appel d'API interne → intégration
- Un parcours utilisateur critique (login, paiement) → e2e
Chapitre 3
Tests unitaires : le pattern AAA
Une fonction, un comportement, un test lisible en 5 secondes. Le pattern Arrange / Act / Assert structure tes tests pour qu'ils restent compréhensibles à long terme.
Un bon test unitaire se lit en cinq secondes. Pour atteindre cette clarté, on suit une structure standard, le pattern AAA : Arrange, Act, Assert. On prépare d'abord les données et le contexte (Arrange), on exécute ensuite la fonction testée (Act), et on vérifie enfin le résultat (Assert). Ces trois sections, séparées visuellement, rendent le test immédiatement compréhensible : n'importe qui voit ce qui est mis en place, ce qui est lancé, et ce qui est attendu. Comme le montre l'exemple ci-dessous, cette régularité fait qu'on n'a plus à déchiffrer chaque test comme une énigme.
Au-delà de la structure, cinq qualités font un bon test unitaire. L'isolation : pas de base de données, pas de réseau, pas d'horloge réelle. Le déterminisme : cent exécutions donnent cent fois le même résultat — un test qui dépend du hasard, de l'heure ou de l'ordre d'exécution devient flaky et ruine la confiance dans toute la suite. La rapidité : on doit pouvoir lancer l'ensemble en quelques secondes. Un nom clair qui décrit le comportement (renvoie 0 quand le panier est vide plutôt que test calcul). Et le principe une seule chose testée par test : un comportement, une raison d'échouer.
Reste une question centrale : comment isoler l'unité testée de ses dépendances externes ? Grâce aux mocks et aux spies. Un mock remplace une dépendance réelle (l'envoi d'un email, un appel réseau, l'horloge) par une fausse version contrôlée, ce qui garde le test rapide et déterministe. Un spy observe si une fonction a bien été appelée, et avec quels arguments — parfait pour vérifier, comme dans l'exemple, qu'une inscription déclenche bien l'envoi de l'email de bienvenue. Un repère utile : si tu te mets à mocker les fonctions internes de ton propre module, c'est souvent le signe que ton découpage mériterait d'être revu.
AAA : Arrange, Act, Assert
import { describe, it, expect } from 'vitest';
import { calculerTVA } from './tva';
describe('calculerTVA', () => {
it('applique 20% sur un montant HT', () => {
// Arrange : préparer les données et le contexte
const ht = 100;
// Act : exécuter la fonction testée
const ttc = calculerTVA(ht, 0.2);
// Assert : vérifier le résultat
expect(ttc).toBe(120);
});
});Les règles d'un bon test unitaire
- Isolation : pas de DB, pas de réseau, pas d'horloge réelle
- Déterministe : 100 exécutions = 100 résultats identiques
- Rapide : on doit pouvoir lancer toute la suite en quelques secondes
- Nom clair :
it('renvoie 0 quand le panier est vide')>it('test calcul') - Une seule chose testée : un test = un comportement attendu
Vrai ou faux ?
Un bon test unitaire doit être déterministe : relancé 100 fois sans changement de code, il doit donner 100 fois le même résultat.
Mocks et spies pour isoler
import { vi } from 'vitest';
import { envoyerEmail } from './email';
import { creerUtilisateur } from './users';
vi.mock('./email');
it('envoie un email de bienvenue à l\'inscription', async () => {
await creerUtilisateur({ email: 'alice@exemple.fr' });
expect(envoyerEmail).toHaveBeenCalledWith({
to: 'alice@exemple.fr',
template: 'bienvenue',
});
});Chapitre 4
Le TDD : Red, Green, Refactor
Écrire le test avant le code, c'est laisser le test guider le design. Le code se dessine plus proprement.
Le TDD (Test-Driven Development, ou développement piloté par les tests) renverse l'ordre habituel : tu écris le test avant le code. Ça semble contre-intuitif au début — comment tester ce qui n'existe pas encore ? — mais c'est justement le point : en décrivant d'abord le comportement attendu, tu laisses le test guider le design de ton code plutôt que l'inverse. Tu réfléchis à ce que ta fonction doit faire et à comment tu veux l'appeler, avant de te perdre dans comment l'implémenter.
Le cycle tient en trois temps, détaillés ci-dessous. Red : tu écris un test qui échoue, parce que le code n'existe pas encore ou ne fait pas ce qu'il faut. Green : tu écris le minimum de code pour passer au vert, sans anticiper, sans sur-concevoir. Refactor : une fois le filet en place, tu nettoies le code et les tests sans changer le comportement. Puis tu recommences pour le comportement suivant. La discipline des baby steps — un comportement à la fois, pas dix cas d'un coup — paraît lente au démarrage, mais aboutit à un code modulaire, testé et sans dette accumulée.
Le bénéfice le moins visible du TDD est aussi le plus précieux : ton interface est pensée depuis l'usage, pas depuis l'implémentation. En écrivant d'abord comment tu aimerais appeler la fonction, tu obtiens des API plus simples et plus intuitives, parce qu'elles ont été conçues du point de vue de celui qui les utilise. L'exemple du panier ci-dessous illustre bien la démarche : le premier test (« un panier vide a un total de 0 ») et son implémentation minimale (« retourne 0 ») paraissent dérisoires, mais c'est en empilant ces petites étapes sous filet que tu construis, fonctionnalité par fonctionnalité, un objet solide et entièrement couvert.
Le cycle en 3 étapes
- Red : tu écris un test qui échoue (la fonction n'existe pas encore, ou ne fait pas ce qu'il faut)
- Green : tu écris le minimum de code pour passer au vert. Pas plus.
- Refactor : tu nettoies le code et les tests, sans changer le comportement
Baby steps
Un comportement à la fois, pas dix cas d'un coup. Tu ajoutes un test, tu fais passer, tu nettoies. Boucle suivante. Au début ça paraît lent, à la fin du projet tu as un design propre, modulaire, testable.
Le bénéfice non-évident du TDD
Exemple : implémenter un panier en TDD
import { describe, it, expect } from 'vitest';
import { Panier } from './panier';
describe('Panier', () => {
it('a un total de 0 quand il est vide', () => {
const panier = new Panier();
expect(panier.total()).toBe(0);
});
});export class Panier {
total() {
return 0;
}
}Oui, c'est ridicule au début. Mais à l'étape 3 (refactor) puis aux étapes suivantes (ajout, suppression, remise), tu vas construire un Panier solide, fonctionnalité par fonctionnalité, toujours sous filet.
Chapitre 5
Tests d'intégration
On vérifie que plusieurs briques s'emboîtent vraiment : la DB, l'API HTTP, le file system, les services externes (mockés ou non).
Les tests unitaires vérifient chaque brique isolément, mais une application peut avoir toutes ses briques correctes et pourtant casser à leurs jointures. C'est le rôle des tests d'intégration : vérifier que plusieurs modules s'emboîtent vraiment — le code et la base de données, une route HTTP et sa logique, un service et le système de fichiers. Ils sont un peu plus lents que les unitaires (ils touchent de vraies ressources) mais bien plus rapides que les e2e, et ils attrapent une classe de bugs qu'aucun test unitaire ne verra : une requête SQL mal formée, un mapping incorrect, un contrat d'API rompu.
Le piège numéro un est de tester contre la base de production : à ne jamais faire, même en lecture seule. On utilise une base dédiée aux tests — Postgres dans un conteneur, SQLite en mémoire, ou un schéma séparé. Le second piège concerne l'isolation entre tests : si un test laisse des données derrière lui, il pollue le suivant, et l'ordre d'exécution se met à influencer les résultats — la porte ouverte aux faux positifs et aux bugs intermittents. La parade est de réinitialiser l'état entre chaque test.
Deux stratégies de remise à zéro coexistent. La plus simple, illustrée ci-dessous, vide les tables concernées avant chaque test (beforeEach). La plus propre et la plus rapide encadre chaque test dans une transaction qu'on annule par un ROLLBACK à la fin : la base revient à un état strictement identique sans qu'on ait à nettoyer manuellement. Pour les tests d'API HTTP, un outil comme supertest te permet d'envoyer de vraies requêtes à ton application et de vérifier le code de statut et le corps de la réponse — tu testes ainsi la route de bout en bout côté serveur, sans lancer de navigateur.
Le piège à éviter
Pas de DB de prod, jamais
test_ séparé. Jamais la DB de prod, même en lecture seule.Le pattern fixtures + rollback
import { describe, it, expect, beforeEach } from 'vitest';
import { db } from './db';
import { creerUtilisateur } from './users';
beforeEach(async () => {
await db.user.deleteMany();
});
it('persiste un utilisateur en base', async () => {
const u = await creerUtilisateur({ email: 'alice@exemple.fr' });
const trouve = await db.user.findUnique({ where: { id: u.id } });
expect(trouve?.email).toBe('alice@exemple.fr');
});Variante plus propre : encadrer chaque test par une transaction qui se termine par un ROLLBACK. Tu repars d'un état strictement identique à chaque fois, sans avoir à supprimer manuellement.
Tests d'API HTTP
import { describe, it, expect } from 'vitest';
import request from 'supertest';
import { app } from './app';
it('POST /commandes crée une commande', async () => {
const res = await request(app)
.post('/commandes')
.send({ produitIds: [1, 2] })
.set('Authorization', 'Bearer test-token');
expect(res.status).toBe(201);
expect(res.body).toMatchObject({ id: expect.any(Number) });
});Chapitre 6
End-to-end avec Playwright
Tu simules un·e vrai·e utilisateur·ice dans un vrai navigateur. Lent mais irremplaçable pour les parcours critiques.
Au sommet de la pyramide, les tests end-to-end (e2e, « de bout en bout ») simulent un·e vrai·e utilisateur·ice dans un vrai navigateur : ils cliquent, remplissent des champs, naviguent, exactement comme une personne le ferait. Ils sont lents et plus fragiles que les autres, mais irremplaçables pour valider qu'un parcours critique fonctionne réellement à travers toute la pile — front, back, base de données comprises. C'est le seul niveau qui teste ce que vit vraiment l'utilisateur·ice final.
L'outil de référence aujourd'hui côté JavaScript/TypeScript est Playwright, et pour de bonnes raisons. Son auto-waiting attend automatiquement qu'un élément soit prêt avant d'agir, ce qui élimine les sleep manuels responsables de la plupart des tests instables. Ses locators sémantiques (getByRole, getByLabel) ciblent les éléments par leur rôle d'accessibilité plutôt que par des classes CSS fragiles. Il tourne sur plusieurs navigateurs en parallèle, et son trace viewer rejoue pas à pas un test rouge en intégration continue — inestimable pour diagnostiquer un échec qu'on ne reproduit pas sur sa machine.
Deux réflexes rendent tes tests e2e robustes. D'abord, privilégie les locators sémantiques aux sélecteurs CSS : si tu changes tes classes Tailwind demain, getByRole('button', { name: 'Valider' }) continue de fonctionner, et en prime tu valides l'accessibilité de ton interface au passage. Ensuite, sois sélectif sur ce que tu testes en e2e : réserve-le aux parcours à fort enjeu (connexion, inscription, paiement, création d'une ressource critique) et laisse de côté les variations de couleur, les survols et les détails visuels, bien trop fragiles à ce niveau. Un e2e ciblé sur l'essentiel reste rapide et digne de confiance.
Pourquoi Playwright en 2025
- Auto-waiting : Playwright attend que l'élément soit prêt, pas de
sleepmanuel - Locators sémantiques :
getByRole,getByLabel, résistants au refactor UI - Multi-navigateurs : Chrome, Firefox, Safari, en parallèle
- Trace viewer : visualisation pas-à-pas d'un test rouge en CI
- Isolation : chaque test = un contexte navigateur neuf
Exemple : parcours d'inscription
import { test, expect } from '@playwright/test';
test('un·e visiteur·euse peut s\'inscrire', async ({ page }) => {
await page.goto('/signup');
// Locators sémantiques : se basent sur l'accessibilité
await page.getByLabel('Email').fill('test@exemple.fr');
await page.getByLabel('Mot de passe').fill('Secret123!');
await page.getByRole('button', { name: 'Créer mon compte' }).click();
// Auto-waiting : on attend que l'écran de bienvenue apparaisse
await expect(
page.getByRole('heading', { name: 'Bienvenue' }),
).toBeVisible();
});Locators sémantiques par défaut
.btn-primary, #submit-1234). Privilégie getByRole, getByLabel, getByText : si tu changes les classes Tailwind demain, tes tests continuent de passer. Bonus : tu valides en même temps l'accessibilité.Ce qui vaut un test e2e
- Login / signup
- Paiement, processus checkout
- Création d'une ressource critique (commande, ticket, dossier)
- Pas : les variations de couleur, les hovers, les détails CSS (trop fragile)
Pour aller plus loin
- Documentation Playwright (Locators, best practices) · playwright.dev (en anglais) (nouvel onglet)
- Jest — Getting Started · jestjs.io (en anglais) (nouvel onglet)
- Write tests, not too many, mostly integration · kentcdodds.com (en anglais) (nouvel onglet)
- Cross-browser testing (MDN) · MDN (en anglais) (nouvel onglet)
Chapitre 7
Couverture de code, l'arme à double tranchant
La couverture est un signal utile, mais ce n'est pas un objectif. 100 % de couverture ne signifie pas 0 bug.
La couverture de code (code coverage) mesure la proportion de ton code réellement exécutée par tes tests. C'est un signal utile — repérer un module entièrement non testé, par exemple — mais c'est aussi l'une des métriques les plus mal comprises. Le piège est d'en faire un objectif plutôt qu'un indicateur : viser un chiffre pour le chiffre pousse à écrire de mauvais tests. Retiens la nuance fondamentale : 100 % de couverture ne signifie absolument pas zéro bug.
Pourquoi ? Parce que la couverture mesure les lignes exécutées, pas la pertinence de tes vérifications. Un test qui appelle une fonction sans écrire le moindre expect() fait monter la couverture… sans rien vérifier du tout. Et même à 100 %, tu ne testes que les cas que tu as imaginés — les cas limites auxquels tu n'as pas pensé restent invisibles. Parmi les métriques disponibles (lignes, fonctions, instructions), la plus instructive est la couverture de branches, qui vérifie que chaque embranchement conditionnel (les deux côtés d'un if) a été emprunté.
En pratique, vise un seuil raisonnable : 70 à 85 % sur le code métier suffit largement ; pousse à 95 % et plus sur une bibliothèque critique (paiement, cryptographie). Mais mieux vaut 80 % avec de vrais tests qu'un faux 100 % obtenu en testant des getters triviaux au détriment des vrais cas limites. Pour aller plus loin dans l'évaluation de la qualité de tes tests, découvre le mutation testing (avec un outil comme Stryker) : il modifie volontairement ton code — transforme un > en <, par exemple — et vérifie si tes tests le détectent. Si tes tests restent verts face à une faute injectée, c'est qu'ils sont faibles. C'est la mesure la plus exigeante qui soit.
Les métriques
- Lines : % de lignes exécutées par les tests (trompeur)
- Branches : % de branches conditionnelles (plus utile)
- Functions : % de fonctions appelées
- Statements : % d'instructions
# Vitest avec couverture
npx vitest run --coverageLe seuil raisonnable
70 à 85 % sur le code métier suffit largement. Pour une lib critique (paiement, crypto), pousse à 95+. Mais ne vise jamais 100 % juste pour le chiffre.
Le piège du 100 %
Vrai ou faux ?
Atteindre 100 % de couverture de code garantit l'absence de bugs.
Mutation testing : la vraie mesure de qualité
Outils comme Stryker : ils modifient ton code volontairement (changent un > en <, par exemple) et vérifient si tes tests le détectent. Si non, tes tests sont faibles. C'est la métrique la plus exigeante.
Chapitre 8
Tests en intégration continue
Si ce n'est pas vert en CI, ce n'est pas mergé. Les tests doivent tourner sur chaque PR, bloquer le merge en cas d'échec.
Des tests ne servent à quelque chose que s'ils sont exécutés automatiquement, à chaque changement, par une machine impartiale. C'est le rôle de l'intégration continue (CI) : à chaque push et à chaque pull request, la CI installe le projet, lance le lint et les tests, et refuse la fusion si quoi que ce soit est rouge. La règle qui doit devenir un réflexe d'équipe : si ce n'est pas vert en CI, ce n'est pas mergé. Ainsi, personne ne peut introduire une régression sur la branche principale sans qu'un test l'en empêche.
Toute la subtilité consiste à ne pas transformer la CI en goulot d'étranglement, car des tests trop lents finissent par être contournés. On organise donc la suite par vitesse, comme le montre le workflow ci-dessous. Les tests unitaires et d'intégration, rapides, sont bloquants sur chaque pull request. Les tests e2e des parcours vraiment critiques le sont aussi. En revanche, la suite e2e complète, plus lente, tourne plutôt une fois par nuit (nightly) avec une alerte en cas d'échec. Ce découpage garde un retour rapide sur chaque contribution sans sacrifier la couverture globale.
Un ennemi mérite une vigilance absolue : le test flaky, celui qui passe parfois et échoue parfois sans le moindre changement de code. Il est bien plus nuisible qu'un test qui échoue franchement, car il érode insidieusement la confiance : à force de voir des rouges « pour rien », l'équipe prend l'habitude de relancer machinalement, et le jour où le rouge signale un vrai bug, personne ne le remarque. La seule bonne réponse est de le diagnostiquer et le réparer tout de suite — jamais de le masquer avec un retry aveugle, un sleep ou un .skip() qui deviendra permanent. Le scénario ci-dessous te fait justement dérouler la bonne façon de traiter un test de paiement instable.
Le workflow type
name: Tests
on:
push:
branches: [main]
pull_request:
jobs:
unit-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run test:unit -- --coverage
- run: npm run test:integration
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-traces
path: test-results/Stratégie selon la vitesse
- Unit + intégration : bloquants sur chaque PR
- E2E rapide (parcours critiques) : bloquant sur chaque PR
- E2E lent (suite complète) : nightly ou hebdo, avec alerte Slack si rouge
Tests flaky
.skip() permanent : tu perds confiance dans toute la suite.🧭 Un test e2e est flaky en CI
Ton test Playwright de paiement échoue une fois sur quatre en CI, sans raison visible. Il repasse au vert quand tu relances le job. L'équipe commence à relancer machinalement. Que fais-tu ?
Le test « checkout » échoue par intermittence. Quelle est ta première décision ?
🛠️ Exercice optionnel
Tester un calcul de TVA en TDD
Tu vas implémenter une fonction calculerTVA(montantHT, taux) en TDD : test d'abord, code ensuite. Trois cas couvrent l'essentiel.
Ta mission
La fonction cible :
export function calculerTVA(montantHT: number, taux: number): number {
// À implémenter
}
Trois tests à écrire (dans cet ordre) :
- Cas nominal : 100 € HT à 20 % de TVA renvoie 120
- Arrondi : 33.33 € HT à 20 % renvoie 39.996, arrondi à 40.00
- Erreur : un montant négatif lève une erreur
"Montant négatif"
Méthode :
- Pour chaque test : écris-le, lance-le (il échoue, Red).
- Implémente le minimum dans
calculerTVApour passer au vert. - Refactore si besoin, sans casser les tests précédents.
- Passe au test suivant.
Tu bloques ? Des indices, à dévoiler quand tu en as besoin.
Indice 1
Indice masqué.
Indice 2
Indice masqué.
Indice 3
Indice masqué.
✅ QCM de fin de cours
Teste tes acquis
10 questions, plusieurs réponses parfois possibles. Coche tout ce qui te semble juste, puis valide pour voir ton score et les explications.
- 1
Quel type de test doit être le plus nombreux dans ta suite ?
- 2
Que signifie le pattern AAA ?
- 3
En TDD, par quoi commences-tu ?
- 4
100 % de couverture garantit-il zéro bug ?
- 5
Que doit-on mocker dans un test unitaire ?
- 6
Quel outil e2e est devenu la référence côté JS/TS en 2025 ?
- 7
Un test flaky, c'est quoi ?
- 8
Quel locator Playwright est à privilégier ?
- 9
Tests d'intégration sur la DB : que faire entre chaque test ?
- 10
Le mutation testing sert à ?
Tu peux laisser des questions sans réponse, elles compteront comme fausses.
🎓 Attestation
Ton attestation de réussite
Termine le QCM avec au moins 80% de bonnes réponses pour débloquer ton attestation de réussite.
Tu veux ce cours pour ton équipe ?
Je peux adapter et animer ce cours pour tes formateur·ices ou tes apprenant·es, en présentiel ou en distanciel. Parlons-en pendant l'audit gratuit.
Réserver un audit gratuit →