Le flux de développement piloté par les tests
Orientez la conception avec le cycle rouge-vert-refactorisation.
Le flux de développement piloté par les tests est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.
TDD est un outil de conception
Le développement piloté par les tests est souvent présenté comme une technique de test, mais sa véritable valeur réside dans la pression exercée sur la conception. En écrivant d'abord le test, vous êtes contraint de définir l'interface publique d'un objet, ses dépendances et son contrat avant d'écrire la moindre implémentation. Les tests sont un sous-produit ; une meilleure conception est le résultat.
Rouge, Vert, Refactorisation
Le cycle comporte trois phases strictes :
- Rouge — écrivez un test en échec pour le prochain comportement minimal. Il doit échouer pour la bonne raison.
- Vert — écrivez le code minimal pour réussir, même s'il est maladroit.
- Refactorisation — nettoyez l'implémentation et les tests tout en restant au vert.
La discipline est importante : ne sautez jamais l'étape Rouge, car vous pourriez ne rien tester, et ne développez jamais plus que nécessaire pendant l'étape Verte.
Rouge : écrire d'abord le test qui échoue
Nous allons construire un PriceCalculator qui applique une remise en pourcentage. Commencez par un test portant sur un comportement qui n'existe pas encore. Son exécution doit échouer parce que la classe n'est pas définie — c'est un Rouge valide.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
public function test_applies_a_percentage_discount(): void
{
$calc = new PriceCalculator();
// 100 with 10% off -> 90.0
self::assertSame(90.0, $calc->withDiscount(100.0, 10));
}
}
Vert : le code minimal pour réussir
Écrivez maintenant la plus petite implémentation qui fait passer le test au vert. Résistez à l'envie d'ajouter des règles d'arrondi, de la validation ou la gestion des devises : aucun test en échec ne les exige encore.
<?php
final class PriceCalculator
{
public function withDiscount(float $amount, float $percent): float
{
return $amount - ($amount * $percent / 100);
}
}
$calc = new PriceCalculator();
var_dump($calc->withDiscount(100.0, 10)); // float(90)
Trianguler pour favoriser la généralité
Un seul test peut être satisfait par un codage en dur. La triangulation — l'ajout d'un second exemple différent — force l'implémentation à se généraliser. Ajoutez un cas que la solution triviale ne peut pas simuler, ce qui fait émerger la véritable formule.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
public function test_zero_percent_is_unchanged(): void
{
self::assertSame(50.0, (new PriceCalculator())->withDiscount(50.0, 0));
}
public function test_full_discount_is_free(): void
{
self::assertSame(0.0, (new PriceCalculator())->withDiscount(50.0, 100));
}
}
Transformer les cas limites en spécifications
En TDD, une nouvelle exigence est un nouveau test en échec. Supposons que les remises négatives soient invalides. Écrivez d'abord le test qui attend une exception ; il échoue parce que rien ne lève encore d'exception. Le test documente le contrat.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorValidationTest extends TestCase
{
public function test_rejects_negative_discount(): void
{
$this->expectException(\InvalidArgumentException::class);
(new PriceCalculator())->withDiscount(100.0, -5);
}
}
Vert à nouveau, puis refactorisation
Ajoutez la condition de protection pour faire passer le nouveau test, puis refactorisez en confiance : les tests existants détectent les régressions. Remarquez que nous n'avons introduit l'arrondi qu'une fois qu'un test, ou une exigence connue, le justifiait.
<?php
final class PriceCalculator
{
public function withDiscount(float $amount, float $percent): float
{
if ($percent < 0 || $percent > 100) {
throw new \InvalidArgumentException('percent must be 0..100');
}
return round($amount - ($amount * $percent / 100), 2);
}
}
var_dump((new PriceCalculator())->withDiscount(19.99, 15)); // float(16.99)
Les fournisseurs de données condensent les exemples
Une fois qu'un comportement est stable, regroupez de nombreuses paires d'exemples dans un seul test paramétré avec un fournisseur de données. La boucle Rouge-Vert reste ainsi rapide et le tableau d'intention demeure lisible.
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;
final class DiscountTableTest extends TestCase
{
#[DataProvider('cases')]
public function test_discounts(float $amount, float $pct, float $expected): void
{
self::assertSame($expected, (new PriceCalculator())->withDiscount($amount, $pct));
}
public static function cases(): array
{
return [
'no discount' => [100.0, 0, 100.0],
'ten percent' => [100.0, 10, 90.0],
'free' => [100.0, 100, 0.0],
];
}
}
Garder la boucle rapide
Le TDD ne fonctionne que si la boucle de rétroaction dure quelques secondes et non quelques minutes. Voici des leviers pratiques :
- Exécutez un seul fichier ou utilisez un filtre :
phpunit --filter test_full_discount_is_free. - Utilisez
--testdoxpour lire les tests comme une spécification de comportement. - Gardez les tests unitaires exempts d'E/S — aucune base de données, aucun réseau ni système de fichiers dans la boucle interne.
vendor/bin/phpunit --testdox --filter PriceCalculatorRefactoriser aussi les tests
La phase de refactorisation couvre la suite de tests, pas seulement le code de production. Supprimez la duplication avec des fonctions utilitaires et setUp(), nommez les tests selon leur comportement et supprimez ceux qui ne vérifient plus rien de pertinent. Les tests sont du code que vous maintiendrez pour toujours — accordez-leur le même soin.
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
private PriceCalculator $calc;
protected function setUp(): void
{
$this->calc = new PriceCalculator(); // shared setup, no duplication
}
public function test_ten_percent(): void
{
self::assertSame(90.0, $this->calc->withDiscount(100.0, 10));
}
}
TDD façonne les dépendances
Comme vous écrivez le test en premier, les dépendances maladroites deviennent immédiatement évidentes. Si une classe est difficile à instancier dans un test, c'est un retour de conception : injectez les collaborateurs au lieu de les instancier avec new à l'intérieur. Le calculateur de remises ci-dessous devient testable en acceptant sa politique d'arrondi, au lieu de la coder en dur — TDD a imposé cette séparation.
<?php
interface RoundingPolicy { public function round(float $v): float; }
final class PriceCalculator
{
public function __construct(private RoundingPolicy $rounding) {}
public function withDiscount(float $amount, float $percent): float
{
return $this->rounding->round($amount - ($amount * $percent / 100));
}
}
// Tests inject a deterministic RoundingPolicy; no hidden global behavior.
Vérification rapide
Quel est l'objectif de l'étape Rouge ?
Récapitulatif
Vous avez pratiqué TDD comme une discipline de conception :
- Rouge-Vert-Refactorisation : test en échec, code minimal, puis nettoyage tout en restant au vert.
- La triangulation impose la généralité ; les nouvelles exigences prennent la forme de nouveaux tests en échec.
- Les fournisseurs de données condensent les cas stables ;
--filter/--testdoxmaintiennent la boucle rapide. - Refactorisez aussi vos tests : ce sont du code qui vivra longtemps.
Ensuite : des doublures de test flexibles avec Mockery.
Questions Fréquemment Posées
La leçon « Le flux de développement piloté par les tests » est-elle gratuite ?
Oui — le texte complet de « Le flux de développement piloté par les tests » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Le flux de développement piloté par les tests » ?
Orientez la conception avec le cycle rouge-vert-refactorisation. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PHP Academy ?
Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Le flux de développement piloté par les tests » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?
Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Le flux de développement piloté par les tests
- Simulations et bouchons avec Mockery
- Tests d’intégration et tests fonctionnels
- Tests de mutation avec Infection