Navigation

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

Par Philippe Le Van (twitter accountplv) Dernière mise à jour : 01 February 2013

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 :

http://www.phpunit.de/manual/current/en/database.html

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