← TDD avec Python : le cycle rouge-vert-refactor

Le cycle rouge-vert-refactor

≈ 20 minutes

Le gain du jour : exécuter le cycle rouge → vert → refactor trois fois de suite sur un exemple minuscule, et sentir la différence avec « coder puis tester après coup ».

Le cycle

Le TDD n’est pas « écrire des tests ». C’est un cycle discipliné, répété en boucles très courtes :

  1. Rouge — écrire un test qui échoue, pour un comportement qui n’existe pas encore.
  2. Vert — écrire le code le plus simple possible qui fait passer le test (tricher est permis).
  3. Refactor — nettoyer le code, tests toujours au vert.

Règle d’or : jamais plus d’un test qui échoue à la fois. Si deux tests sont rouges, on est allé trop vite.

Dans un vrai projet, le test s’écrit dans un fichier test_*.py et se lance avec pytest. Ici, sur cette page, on simule le même geste : le champ tests ci-dessous joue le rôle du fichier de test (assert nu, comme pytest les réécrit), et tu écris le code de production juste au-dessus. Lis d’abord l’assertion, imagine-la « rouge », puis écris le code minimal pour la faire passer.

Source recommandée

Kent Beck, Test-Driven Development: By Example — le livre qui a défini ce cycle. Les premiers chapitres suffisent pour aujourd’hui.

L’exemple : un compte bancaire minimal

On construit une classe CompteBancaire avec juste assez de comportement pour voir le cycle tourner : un solde qui démarre à zéro, un dépôt, un retrait qui refuse si le solde est insuffisant.

Tour 1 — solde initial

Rouge : assert compte.solde == 0 échouerait avec NameError si CompteBancaire n’existait pas encore — c’est le bon type d’échec, pas une faute de frappe dans le test.

Vert : le code le plus simple qui satisfait ce test.

🐍 Python dans le navigateur Tour 1 : le solde démarre à zéro

Tour 2 — un dépôt

Rouge d’abord : imagine compte.deposer(50) sans que la méthode existe → AttributeError. Vert : ajoute juste ce qu’il faut, rien de plus (pas de retirer par anticipation).

🐍 Python dans le navigateur Tour 2 : déposer augmente le solde

Tour 3 — retrait refusé

🐍 Python dans le navigateur Tour 3 : un retrait supérieur au solde est refusé

Refactor. Relis les trois tours : rien ne crie encore « dupliqué » ou « confus » à ce stade — trois tours, c’est court. Le réflexe à ancrer aujourd’hui : après chaque vert, on regarde s’il y a lieu de nettoyer, même si souvent la réponse est non.

Remarque le style classiciste ici : aucun double de test (mock), aucun stub. CompteBancaire est un objet réel, on vérifie son état (solde) après l’action — c’est la vérification par état dont parle Martin Fowler dans « Mocks Aren’t Stubs », par opposition au style mockiste qui vérifie des appels plutôt que de l’état.

Vérifie ta compréhension

Laquelle des trois étapes du cycle autorise explicitement à écrire du code « en trichant » (valeur en dur, par exemple) ?

Pourquoi un NameError est-il un « bon » rouge, contrairement à un rouge causé par une faute de frappe dans le test ?

À toi de jouer

Exercice

Ajoute un 4ᵉ tour, toi-même, dans l’éditeur ci-dessous : un dépôt d’un montant négatif doit lever une exception MontantInvalide. Écris d’abord le test (le champ tests), imagine-le rouge, puis seulement écris le code.

🐍 Python dans le navigateur Tour 4 : un dépôt négatif est refusé (à toi de compléter)

Une fois vert, relis les 4 exercices : y a-t-il une duplication qui mériterait un refactor (par exemple factoriser la création du compte, ou la vérification de montant) ? Pose la question ici si un point du cycle reste flou — c’est exactement le rôle de ce dialogue.

🗂 Fiche de référence associée : Cycle TDD & pytest — antisèche.