Il existe deux types de tests automatisés dans Symfony: les tests unitaires et les tests fonctionnels. Les tests unitaires vérifient que chaque méthode et fonction fonctionne correctement. Chaque test doit être aussi indépendant que possible des autres. D'autre part, les tests fonctionnels vérifient que l'application résultante se comporte correctement dans son ensemble.
Les tests unitaires seront couverts dans ce chapitre, alors que le prochain chapitre sera consacré aux tests fonctionnels.
Symfony2 s'intègre à une bibliothèque indépendante, PHPUnit, pour vous donner un framework de tests riche. Pour exécuter des tests, vous devrez installer PHPUnit 3.5.11 ou une version ultérieure.
Chaque test - qu'il s'agisse d'un test unitaire ou un test fonctionnel - est une classe PHP qui doit se situer dans un sous-répertoire Tests/ de vos paquets. Si vous suivez cette règle, vous pouvez exécuter tous les tests de votre application avec la commande suivante:
$ phpunit -c app/
L'option -c indique à PHPUnit de chercher un fichier de configuration dans le répertoire app/. Si vous êtes curieux de connaître les options de PHPUnit, consultez le fichier app/phpunit.xml.dist.
Un test unitaire est généralement un test contre une classe PHP spécifique. Commençons par écrire des tests pour la méthode Jobeet::slugify().
Créez un nouveau fichier, JobeetTest.php, dans le répertoire src/Ens/JobeetBundle/Tests/Utils. Par convention, le sous-répertoire Tests/ doit répliquer le répertoire de votre paquet. Donc, quand nous testons une classe dans le répertoire Utils/ de notre paquet, nous avons mis le test dans le répertoire Tests/Utils/:
// src/Ens/JobeetBundle/Tests/Utils/JobeetTest.php namespace Ens\JobeetBundle\Tests\Utils; use Ens\JobeetBundle\Utils\Jobeet; class JobeetTest extends \PHPUnit_Framework_TestCase { public function testSlugify() { $this->assertEquals('sensio', Jobeet::slugify('Sensio')); $this->assertEquals('sensio-labs', Jobeet::slugify('sensio labs')); $this->assertEquals('sensio-labs', Jobeet::slugify('sensio labs')); $this->assertEquals('paris-france', Jobeet::slugify('paris,france')); $this->assertEquals('sensio', Jobeet::slugify(' sensio')); $this->assertEquals('sensio', Jobeet::slugify('sensio ')); } }
Pour exécuter ce test, vous pouvez utiliser la commande suivante:
phpunit -c app/ src/Ens/JobeetBundle/Tests/Utils/JobeetTest
Comme tout devrait fonctionner correctement, vous devriez obtenir le résultat suivant:
PHPUnit 3.6.10 by Sebastian Bergmann. Configuration read from /home/dragos/work/jobeet/app/phpunit.xml.dist . Time: 0 seconds, Memory: 3.50Mb OK (1 test, 6 assertions)
Pour une liste complète des assertions, vous pouvez consulter la documentation de PHPUnit.
Le jeton pour une chaîne vide est une chaîne vide. Vous pouvez le tester, ça va marcher. Mais une chaîne vide dans une URL n'est pas une bonne idée. Nous allons changer la méthode slugify() de sorte qu'elle retourne la chaîne "n-a" dans le cas d'une chaîne vide.
Vous pouvez écrire le premier test, puis mettre à jour la méthode, ou l'inverse. C'est vraiment une question de goût mais l'écriture du test en premier vous donne l'assurance que votre code implémente réellement ce que vous avez prévu:
// src/Ens/JobeetBundle/Tests/Utils/JobeetTest.php // ... $this->assertEquals('n-a', Jobeet::slugify('')); // ...
Maintenant, si nous réexécutons le test, nous aurons un échec:
PHPUnit 3.6.10 by Sebastian Bergmann. Configuration read from /home/dragos/work/jobeet/app/phpunit.xml.dist F Time: 0 seconds, Memory: 3.50Mb There was 1 failure: 1) Ens\JobeetBundle\Tests\Utils\JobeetTest::testSlugify Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'n-a' +'' /home/dragos/work/jobeet/src/Ens/JobeetBundle/Tests/Utils/JobeetTest.php:17 FAILURES! Tests: 1, Assertions: 7, Failures: 1.
Maintenant, modifiez la classe Jobeet et ajoutez la condition suivante au début:
// src/Ens/JobeetBundle/Utils/Jobeet.php // ... static public function slugify($text) { if (empty($text)) { return 'n-a'; } // ... }
Le test doit maintenant passer comme prévu, et vous pourrez profiter de la barre verte.
Disons que le temps a passé et l'un de vos utilisateurs vous rapporte un bug bizarre: certains liens des offres pointent vers une page d'erreur 404. Après quelques recherches, vous trouverez que, pour une raison quelconque, ces offres ont un jeton société, intitulé ou lieu vide.
Comment est-ce possible?
Vous regardez à travers les enregistrements de la BDD et les colonnes ne sont absolument pas vide. Vous y réfléchissez un moment, et hop, vous trouvez la cause. Lorsqu'une chaîne ne contient que des caractères non-ASCII, la méthode slugify() la convertit en une chaîne vide. Je suis si heureux d'avoir trouvé la cause, vous ouvrez la classe Jobeet et corrigez le problème tout de suite. C'est une mauvaise idée. Tout d'abord, nous allons ajouter un test:
$this->assertEquals('n-a', Jobeet::slugify(' - '));
Après avoir vérifié que le test ne passe pas, modifiez la classe Jobeet et déplacez la vérification de la chaîne vide à la fin de la méthode:
static public function slugify($text) { // ... if (empty($text)) { return 'n-a'; } return $text; }
Le nouveau test passe désormais, comme tous les autres. La méthode slugify() a eu un bug en dépit de notre couverture à 100%.
Vous ne pouvez pas penser à tous les cas lors de l'écriture des tests, et c'est très bien. Mais quand vous en découvrez un, vous devez écrire un test avant de corriger votre code. Cela signifie également que votre code va s'améliorer au fil du temps, ce qui est toujours une bonne chose.
Vous savez probablement que Symfony a été créé par des français, nous allons donc ajouter un test avec un mot français qui contient un accent:
$this->assertEquals('developpeur-web', Jobeet::slugify('Développeur Web'));
Le test doit échouer. Au lieu de remplacer é par e, la méthode slugify() l'a remplacé par un tiret (-). C'est un problème compliqué, appelé translittération. Heureusement, la bibliothèque iconv, si elle est installée, va faire le travail pour nous. Remplacez le code de la méthode slugify() par ce qui suit:
static public function slugify($text) { // replace non letter or digits by - $text = preg_replace('#[^\\pL\d]+#u', '-', $text); // trim $text = trim($text, '-'); // transliterate if (function_exists('iconv')) { $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); } // lowercase $text = strtolower($text); // remove unwanted characters $text = preg_replace('#[^-\w]+#', '', $text); if (empty($text)) { return 'n-a'; } return $text; }
N'oubliez pas de sauvegarder tous vos fichiers PHP avec l'encodage UTF-8, car c'est l'encodage par défaut de Symfony, et celui utilisé par iconv pour faire la translitération.
Modifiez également le fichier de test pour n'exécuter le test que si iconv est disponible:
if (function_exists('iconv')) { $this->assertEquals('developpeur-web', Jobeet::slugify('Développeur Web')); }