Introduction : Pourquoi tester en PHP ?
\n\nDévelopper en PHP sans tests, c'est comme construire une maison sans fondations. Tôt ou tard, l'application plantera et les utilisateurs verront « l'écran blanc de la mort ». Tester le code n'est pas un luxe, mais une nécessité pour tout projet sérieux. Cela permet de :
\n- \n
- Détecter les bugs à un stade précoce — avant que le code ne soit mis en production. \n
- Simplifier le refactoring — vous pouvez modifier l'architecture en toute confiance, sachant que les tests détecteront les régressions. \n
- Créer un code auto-documenté — les tests servent de documentation vivante sur le fonctionnement attendu du système. \n
- Renforcer la confiance de l'équipe — chaque nouveau commit est automatiquement vérifié. \n
En PHP, il existe trois niveaux principaux de tests : Unit (tests unitaires), Integration (tests d'intégration) et E2E (tests de bout en bout). Dans cet article, nous allons détailler chacun d'eux avec des exemples pratiques, des simples assertions aux tests navigateurs complets.
\n\n1. Tests unitaires : tester les « briques » de l'application
\n\nQu'est-ce qu'un test unitaire ?
\nUn test unitaire vérifie le fonctionnement d'un petit bloc de code — généralement une méthode ou une fonction — en isolation totale des dépendances externes (base de données, API, système de fichiers).
\n\nOutils pour PHP
\n- \n
- PHPUnit — le standard de facto. Utilisé dans Laravel, Symfony et de nombreux CMS. \n
- Pest — une alternative moderne avec une syntaxe plus concise, construite au-dessus de PHPUnit. \n
Exemple : tester un calculateur de remises
\nSupposons que nous ayons une classe simple DiscountCalculator :
<?php\n\nclass DiscountCalculator\n{\n public function calculate(float $price, float $percent): float\n {\n if ($percent < 0 || $percent > 100) {\n throw new \\InvalidArgumentException('Le pourcentage doit être compris entre 0 et 100');\n }\n return $price - ($price * $percent / 100);\n }\n}\n\n\nÉcrivons maintenant un test unitaire avec PHPUnit :
\n\n<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass DiscountCalculatorTest extends TestCase\n{\n private DiscountCalculator $calculator;\n\n protected function setUp(): void\n {\n $this->calculator = new DiscountCalculator();\n }\n\n public function testCalculateWithZeroPercent(): void\n {\n $result = $this->calculator->calculate(100, 0);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateWithFiftyPercent(): void\n {\n $result = $this->calculator->calculate(200, 50);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateThrowsExceptionOnInvalidPercent(): void\n {\n $this->expectException(\\InvalidArgumentException::class);\n $this->calculator->calculate(100, 150);\n }\n}\n\n\nPoints clés :
\n- \n
- Chaque méthode teste un seul scénario. \n
- Nous n'accédons pas à la base de données ni aux services externes. \n
- Nous utilisons
setUp()pour initialiser les objets communs. \n
Bonnes pratiques des tests unitaires
\n- \n
- Suivez le principe FIRST : Fast (rapide), Isolated (isolé), Repeatable (reproductible), Self-validating (auto-vérifiant), Timely (opportun). \n
- Nommez les tests comme « test_action_résultatAttendu ». \n
- Évitez la logique dans les tests — pas de if/switch/boucles. \n
2. Tests d'intégration : vérifier l'interaction des composants
\n\nQuelle est la différence avec les tests unitaires ?
\nLes tests d'intégration vérifient comment différentes parties du système fonctionnent ensemble. Par exemple, comment votre service interagit avec une vraie base de données, un cache ou une API externe.
\n\nExemple : tester un repository avec une base de données (Laravel + SQLite)
\nSupposons que nous ayons un <