Versions
09/06/2012: création
01/02/2013: composer & sf2.1
Contactez-nous
Kitpages
17 rue de la Frise
38000 Grenoble
tel : 04 58 00 33 81
Tests unitaire d'un bundle Symfony2 avec Doctrine2
Introduction
Ce tutoriel fait partie du cycle "Tests unitaires d'un bundle Symfony2"
Cette page montre un exemple de test de base de données dans un bundle symfony2 utilisant doctrine2.
Les tests unitaires avec une base de données, c'est assez complexe à gérer. Je vous conseille fortement la lecture de cette page de la doc de phpunit :
Installation de l'environnement
phpunit/DbUnit
Il faut installer une extension de PHPUnit : phpunit/DbUnit
[root@vbox53 ~]# pear install phpunit/DbUnit downloading DbUnit-1.1.2.tgz ... Starting to download DbUnit-1.1.2.tgz (41,895 bytes) ............done: 41,895 bytes install ok: channel://pear.phpunit.de/DbUnit-1.1.2 [root@vbox53 ~]#
Installation des BeberleiDoctrineExtensions en local
Note : ces extensions doctrine n'ont rien à voir avec les gedmo-doctrine-extensions
Ajouter la ligne suivante dans le require-dev de votre composer.json :
"beberlei/DoctrineExtensions": "dev-master"
et faire un composer update --dev
Il faut mettre à jour le fichier .travis.yml pour installer phpunit/DbUnit
language: php php: - 5.3 - 5.4 before_script: - pyrus channel-discover pear.symfony.com - pyrus install --force phpunit/DbUnit - composer install --dev --prefer-source script: phpunit --coverage-text notifications: email: - travis-ci@kitpages.fr
Les outils utilisés
Là on aborde la partie la plus complexe du tutoriel.
Par défaut dans un test unitaire, on utilise PHPUnit pour faire ses tests. Là on va ajouter 3 couches d'abstractions au dessus de PHPUnit pour avoir un accès facile à Doctrine2 sur une base toujours propre.
Voilà les niveaux d'abstraction successifs :
- phpunit : la librairie de tests de base
- DbUnit : une extension de phpunit dans pear qui facilite les tests avec une base de données
- BeberleiDoctrineExtensions : une extension au dessus de DbUnit pour accéder simplement à son EntityManager dans ses tests
- Une couche maison (le code est un peu plus loin) qui permet d'initialiser l'EntityManager correctement pour notre bundle et nos tests
Couche d'abstraction maison, le BundleOrmTestCase
Cette couche a pour objectif :
- De renvoyer une instance de l'entity manager
- De créer le schéma de base de données en fonction des entités du bundle à tester
- Vider et remplir de nouveau la base avec des fixtures à chaque test
Cette couche contient pas mal de fichiers, voyons d'abord l'arborescense :
Kitpages/DataGridBundle/Tests/ // racine _doctrine/ config/ // les entités supplémentaires dont on a besoin spécifiquement dans les tests Node.orm.xml dataset/ entityFixture.xml // les données à inséerer dans la base BundleOrmTestCase.php // la création de l'entity manager SchemaSetupListener.php // la création du schéma
BundleOrmTestCase
Les fichiers de tests ayant besoin de Doctrine étendront cette classe.
<?php namespace Kitpages\DataGridBundle\Tests; use Kitpages\DataGridBundle\Tests\SchemaSetupListener; use Doctrine\ORM\EntityManager; use Doctrine\Common\EventManager; use Doctrine\ORM\Configuration; use Doctrine\Common\Cache\ArrayCache; use DoctrineExtensions\PHPUnit\OrmTestCase; class BundleOrmTestCase extends OrmTestCase { /** * @return \Doctrine\ORM\EntityManager */ protected function createEntityManager() { // event manager used to create schema before tests $eventManager = new EventManager(); $eventManager->addEventListener(array("preTestSetUp"), new SchemaSetupListener()); // doctrine xml configs and namespaces $configPathList = array(); if (is_dir(__DIR__.'/../Resources/config/doctrine')) { $dir = __DIR__.'/../Resources/config/doctrine'; $configPathList[] = $dir; $prefixList[$dir] = 'Kitpages\DataGridBundle\Entities'; } if (is_dir(__DIR__.'/_doctrine/config')) { $dir = __DIR__.'/_doctrine/config'; $configPathList[] = $dir; $prefixList[$dir] = 'Kitpages\DataGridBundle\Tests\TestEntities'; } // create drivers (that reads xml configs) $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver($prefixList); // sf 2.0 version // $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver($configPathList); // $driver->setNamespacePrefixes($prefixList); // create config object $config = new Configuration(); $config->setMetadataCacheImpl(new ArrayCache()); $config->setMetadataDriverImpl($driver); $config->setProxyDir(__DIR__.'/TestProxies'); $config->setProxyNamespace('Kitpages\DataGridBundle\Tests\TestProxies'); $config->setAutoGenerateProxyClasses(true); //$config->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger()); // create entity manager $em = EntityManager::create( array( 'driver' => 'pdo_sqlite', 'path' => "/tmp/sqlite-test.db" ), $config, $eventManager ); return $em; } protected function getDataSet() { return $this->createFlatXmlDataSet(__DIR__."/_doctrine/dataset/entityFixture.xml"); } }
SchemaSetupListener.php
Ce fichier est appelé automatiquement par le BundleOrmTestCase
<?php namespace Kitpages\DataGridBundle\Tests; use DoctrineExtensions\PHPUnit\Event\EntityManagerEventArgs; use Doctrine\ORM\Tools\SchemaTool; use Doctrine\ORM\EntityManager; class SchemaSetupListener { public function preTestSetUp(EntityManagerEventArgs $eventArgs) { $em = $eventArgs->getEntityManager(); $schemaTool = new SchemaTool($em); $cmf = $em->getMetadataFactory(); $classes = $cmf->getAllMetadata(); $schemaTool->dropDatabase(); $schemaTool->createSchema($classes); } }
_doctrine/dataset/entityFixture.xml
Ce fichier contient l'ensemble des données de la base générée à chaque fois qu'on lance un nouveau test.
<?xml version="1.0" ?> <dataset> <node id="1" content="Hello buddy!" user="joe" created_at="2010-04-24 17:15:23" parent_id=""/> <node id="2" content="I like it!" user="toto" created_at="2010-04-26 12:14:20" parent_id="1"/> </dataset>
_doctrine/config/Node.orm.xml
Notre KitpagesDataGridBundle est un peu particulier, nous devons créer une entité de toute pièce en dehors du bundle pour pouvoir tester certains fonctions du bundle. Ici, nous créeons une entité "Node" bidon qui nous servira dans les tests.
C'est un fichier de mapping doctrine comme vous en avez vu beaucoup.
<?xml version="1.0" encoding="UTF-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="Kitpages\DataGridBundle\Tests\TestEntities\Node" table="node"> <id name="id" type="integer" column="id"/> <field name="content" column="content" type="text" nullable="true" /> <field name="user" column="user" type="text" nullable="true" /> <field name="createdAt" column="created_at" type="datetime" /> <field name="parentId" column="parent_id" type="integer" /> </entity> </doctrine-mapping>
Notre premier test avec Doctrine !
Commençons par un test ridicule.
<?php namespace Kitpages\DataGridBundle\Tests\Model; use Kitpages\DataGridBundle\Tests\BundleOrmTestCase; class GridManagerTest extends BundleOrmTestCase { public function testConstructor() { $this->assertTrue(true); } }
lancez phpunit à la racine du bundle et voyez si ça marche.
Vous devriez avoir un retour de ce genre.
[webadmin@vbox53 DataGridBundle]$ phpunit 4PHPUnit 3.6.11 by Sebastian Bergmann. Configuration read from /home/webadmin/eclipse/kit-site/vendor/Kitpages/DataGridBundle/phpunit.xml.dist .... Time: 0 seconds, Memory: 6.25Mb OK (4 tests, 12 assertions) [webadmin@vbox53 DataGridBundle]$
Le premier vrai test
GridManagerTests.php
C'est un test utilisant notre système.
On récupère l'entity manager avec $this->getEntityManager() et ensuite on fait ce qu'on veut avec.
Notons qu'à chaque test on a donc une base propre et initalisée avec les données de entityFixture.xml
<?php namespace Kitpages\DataGridBundle\Tests\Model; use Kitpages\DataGridBundle\Model\Field; use Kitpages\DataGridBundle\Model\PaginatorConfig; use Kitpages\DataGridBundle\Service\GridManager; use Kitpages\DataGridBundle\Tests\BundleOrmTestCase; class GridManagerTest extends BundleOrmTestCase { public function testPaginator() { // create queryBuilder $em = $this->getEntityManager(); $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); $queryBuilder = $repository->createQueryBuilder("node"); $queryBuilder->select("node"); // create EventDispatcher mock $service = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcher'); // create Request mock (ok this is not a mock....) $request = new \Symfony\Component\HttpFoundation\Request(); // create gridManager instance $gridManager = new GridManager($service); // configure paginator $paginatorConfig = new PaginatorConfig(); $paginatorConfig->setCountFieldName("node.id"); // get paginator $paginator = $gridManager->getPaginator($queryBuilder, $paginatorConfig, $request); // tests $this->assertEquals(2, $paginator->getTotalItemCount()); } }
Lancer les tests
Sur votre poste, il suffit d'aller à la racine du bundle et de lancer
phpunit
Et pour travis-ci, il suffit de pusher vos modifs sur github et travis-ci lance les tests tout seul comme un grand
Vous recevez ensuite le rapport de tests par email.
Conclusion
N'hésitez pas à compléter l'article dans les commentaires !
Maintenant qu'on sait créer des tests et les lancer, voyons comment évaluer si notre bundle est bien testé ou non. Le taux de couverture.
Commentaires
Note : on ne peut plus ajouter de commentaire sur ce site