• Ce blog — désormais archivé — est en lecture seule.

Tests unitaires et fonctionnels sur un Bundle en Symfony2

Bonjour,

Depuis quelques temps maintenant je travaille à temps complet avec Symfony2. J’ai écrit quelques bundles puisque, à l’heure actuelle, il n’y a pas autant d’outils fournis avec Symfony2 qu’avec symfony 1.4.

Ecrire un bundle n’est pas très compliqué mais pour bien le tester, on peut vite rencontrer des problèmes. Jusqu’à présent, j’utilisais une manière naïve d’exécuter mes tests PHPUnit. Mes fichiers bootstrap.php allaient chercher le fichier autoload.php dans le projet courant.

Oui, projet courant = forte dépendance.

Aucun moyen de tester mon bundle en « vase clos » et c’est fort dommage. J’ai cherché un petit peu et j’ai trouvé une solution de contournement grâce à l’ami Kris Wallsmith qui explique comment il teste son bundle Symfony2. La solution est bonne puisque il me suffit d’avoir une arborescence de ce type pour tester aisément mes bundles:

projects
  |
  |_ Symfony2 (sources)
  |_ Bundle 1
  |_ Bundle 2
  |_ etc

Ainsi, je configure un phpunit.xml pour chaque bundle qui modifie la variable $_SEVER['SYMFONY'] de la sorte:

<php>
    <server name="SYMFONY" value="../Symfony2/src" />
</php>

Cette configuration permet de s’affranchir d’un projet complet pour les tests unitaires. Seulement j’ai rencontré deux problèmes:

  • Impossible de faire des tests fonctionnels.
  • Impossible de tester le bundle en intégration continue.

Or, l’intégration continue pour moi, c’est vital (ou presque). J’ai donc cherché une meilleure solution qui prend en compte les deux problèmes ci-dessus.

Tests unitaires d’un bundle

Pour les tests unitaires, c’est assez simple et assez proche de la méthode de Kris. La différence vient du fait que le bundle embarque les sources de Symfony2 en fixtures.

Le fichier phpunit.xml.dist prend cette forme:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
   backupStaticAttributes="false"
   colors="true"
   convertErrorsToExceptions="true"
   convertNoticesToExceptions="true"
   convertWarningsToExceptions="true"
   processIsolation="false"
   stopOnFailure="false"
   syntaxCheck="false"
   bootstrap="./Tests/bootstrap.php">
    <php>
        <server name="SYMFONY" value="./Fixtures/symfony/src" />
    </php>
    <testsuites>
        <testsuite name="MyBundle Test Suite">
            <directory suffix="Test.php">./Tests</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory>./</directory>
            <exclude>
                <directory>./Tests</directory>
            </exclude>
        </whitelist>
    </filter>
</phpunit>

Le fichier Tests/bootstrap.php par défaut est celui-ci:

<?php

require_once $_SERVER['SYMFONY'].'/Symfony/Component/ClassLoader/UniversalClassLoader.php';

use Symfony\Component\ClassLoader\UniversalClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespace('Symfony', $_SERVER['SYMFONY']);
$loader->register();

spl_autoload_register(function($class)
{
    if (0 === strpos($class, 'My\\Bundle\')) {
        $path = implode('
/', array_slice(explode('\', $class), 2)).'.php';
        require_once __DIR__.'
/../'.$path;
        return true;
    }
});

Et pour terminer, il faudra ajouter le repo Git de Symfony2 en submodule du bundle dans Fixtures/symfony:

git submodule add git://github.com/symfony/symfony.git  Fixtures/symfony

Pour lancer les tests unitaires, nous n’avons plus qu’à faire:

git submodule update --init
phpunit -c phpunit.xml.dist

Si l’on souhaite tester son bundle sur la dernière version de Symfony2 en permanence, ce qui semble une bonne idée pour avoir un bundle à jour, on peut intercaler la commande suivante entre les deux précédentes:

git submodule foreach git pull origin master

Voilà pour cette partie, un aperçu avec le JqueryUiBundle:

Tests fonctionnels

Pour les tests fonctionnels, il nous faut un projet en fixtures comme on le faisait en symfony 1.x. Mais ce qui est bien, c’est que nous n’avons pas besoin de tout et que l’on peut customiser son projet de test. On peut probablement enlever les dossiers web/ et src/ et retirer le plus de vendors possible. L’important est d’avoir la console qui fonctionne, signe que le projet reste cohérent.

Pour cela, on va créer un projet dans Fixtures/testProject. Ce projet pourra être modifié à votre guise mais n’oubliez pas que c’est le projet qui sert pour les tests ! Pour rester à jour au niveau des vendors (Symfony, Twig, …) le mieux est de les ajouter en submodules du bundle. Ainsi, on exécutera les mêmes commandes pour lancer nos tests.

Note: Ce projet de test ne doit pas embarquer le bundle dans lequel il se trouve.

Le fichier phpunit.xml.dist va rester celui préconisé dans la documentation officielle de Symfony2.

Mais le fichier Tests/bootstrap.php va changer:

<?php

require_once __DIR__.'/../Fixtures/testProject/app/autoload.php';

$filesystem = new \Symfony\Component\HttpKernel\Util\Filesystem();
$filesystem->remove(__DIR__.'/../Fixtures/testProject/app/cache');
$filesystem->remove(__DIR__.'/../Fixtures/testProject/app/logs');
$filesystem->mkdir(__DIR__.'/../Fixtures/testProject/app/cache');
$filesystem->mkdir(__DIR__.'/../Fixtures/testProject/app/logs');

Celui-ci va permettre de réinitialiser le cache et les logs avant chaque phase de tests.

Dernière modification à effectuer sur le fichier Fixtures/testProject/app/autoload.php du projet de test en ajoutant le code ci-dessous à la fin:

spl_autoload_register(function($class) {
    if (0 === strpos($class, 'My\Bundle\')) {
        $path = implode('
/', array_slice(explode('\', $class), 2)).'.php';
        require_once __DIR__.'
/../../../'.$path;
        return true;
    }
});

Voilà, avec cette configuration, nous pouvons lancer nos tests fonctionnels sans soucis et de manière propre puisque l’environnement qui sera créé est bien celui de notre projet de test que nous maîtrisons bien. On peut ajouter de la configuration dans ce projet de test comme le chargement de routes, le paramétrage du bundle, etc…

Exemple d’utilisation avec le ExposeRoutingBundle:

Avec ces deux astuces, tout bundle peut être testé de manière autonome et dans la vision « à la Symfony2″, c’est plutôt intéressant :-)

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Twitter
  • Google Bookmarks
  • FriendFeed
  • LinkedIn
  • MySpace
  • Netvibes
  • PDF
  • Ping.fm
  • RSS
  • Technorati
  • viadeo FR
  • Wikio
  • Yahoo! Buzz

Related Posts

Cet article a été publié dans Symfony2 avec les mots-clefs : , , , . Bookmarker le permalien. Les commentaires et les trackbacks sont fermés.