Test your doctrine repository using a SQLite database (English)

2013-08-19

  symfony    doctrine    test    english 

While my girlfriend is sick today, I want to write a new article blog post about how to test your doctrine repositories using a SQLite database.

Data returned by your repositories (queries) are really often sensitive and often depends on business rules.

That's why this is important to test your doctrine repositories.

Configuration

Start by configuring Symfony to use a SQLite database for your test environment.

Edit your file app/config/config_test.yml in order to specify to Doctrine to use the SQLite driver:


imports:
    - { resource: config_dev.yml }

doctrine:
    dbal:
        default_connection: default

        connections:
            default:
                driver:  pdo_sqlite
                user:    test
                path:    %kernel.root_dir%/sqlite.db.cache
                #memory: true
                charset: utf8
    orm:
        entity_managers:
            default:
                metadata_cache_driver: apc
                query_cache_driver:    apc
                result_cache_driver:   apc

Please note that the database file will here be stored here: app/sqlite.db.cache (when it will be initialized).

Now, generate our SQLite test database by typing:


$ php app/console doctrine:schema:update --env=test --force
Updating database schema...
Database schema updated successfully! "109" queries were executed

Your SQLite test database is now ready. Let's write our unit test.

Writing repository unit test

We have here a class named DoctrineTestCase which will initialize repository in test environment and we will make extend by all of our repository tests class:


namespace Eko\\MyBundle\\Tests\\Repository;

use Doctrine\\Common\\DataFixtures\\Purger\\ORMPurger;
use Doctrine\\Common\\DataFixtures\\Executor\\ORMExecutor;
use Doctrine\\Common\\DataFixtures\\Loader;

use Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase;

/**
 * Class DoctrineTestCase
 *
 * This is the base class to load doctrine fixtures using the symfony configuration
 */
class DoctrineTestCase extends WebTestCase
{
    /**
     * @var \\Symfony\\Component\\DependencyInjection\\Container
     */
    protected $container;

    /**
     * @var \\Doctrine\\ORM\\EntityManager
     */
    protected $em;

    /**
     * @var string
     */
    protected $environment = 'test';

    /**
     * @var bool
     */
    protected $debug = true;

    /**
     * @var string
     */
    protected $entityManagerServiceId = 'doctrine.orm.entity_manager';

    /**
     * Constructor
     *
     * @param string|null $name     Test name
     * @param array       $data     Test data
     * @param string      $dataName Data name
     */
    public function __construct($name = null, array $data = array(), $dataName = '')
    {
        parent::__construct($name, $data, $dataName);

        if (!static::$kernel) {
            static::$kernel = self::createKernel(array(
                'environment' => $this->environment,
                'debug'       => $this->debug
            ));
            static::$kernel->boot();
        }

        $this->container = static::$kernel->getContainer();
        $this->em = $this->getEntityManager();
    }

    /**
     * Executes fixtures
     *
     * @param \\Doctrine\\Common\\DataFixtures\\Loader $loader
     */
    protected function executeFixtures(Loader $loader)
    {
        $purger = new ORMPurger();
        $executor = new ORMExecutor($this->em, $purger);
        $executor->execute($loader->getFixtures());
    }

    /**
     * Load and execute fixtures from a directory
     *
     * @param string $directory
     */
    protected function loadFixturesFromDirectory($directory)
    {
        $loader = new Loader();
        $loader->loadFromDirectory($directory);
        $this->executeFixtures($loader);
    }

    /**
     * Returns the doctrine orm entity manager
     *
     * @return object
     */
    protected function getEntityManager()
    {
        return $this->container->get($this->entityManagerServiceId);
    }
}

As you can see, this class will load our future Doctrine fixtures. Useful to define multiple tests cases :)

Now, let's create an imaginate repository called CountryRepository of an entity Country which permit to register countries.


namespace Eko\\MyBundle\\Tests\\Repository;

use Eko\\MyBundle\\Tests\\Repository\\DoctrineTestCase;

/**
 * Test the country repository
 */
class CountryRepositoryTest extends DoctrineTestCase
{
    /**
     * Set up repository test
     */
    public function setUp()
    {
        $this->loadFixturesFromDirectory(__DIR__ . '/DataFixtures');
    }

    /**
     * Test finding all countries ordered
     */
    public function testFindAllOrdered()
    {
        $countries = $this->getRepository()->findAllOrdered();

        $this->assertCount(3, $countries, 'Should return 3 countries');

        $this->assertEquals('EN', $countries[0]->getCountryCode());
        $this->assertEquals('FR', $countries[1]->getCountryCode());
        $this->assertEquals('IT', $countries[2]->getCountryCode());
    }

    /**
     * Returns repository
     *
     * @return \\Eko\\MyBundle\\Repository\\CountryRepository
     */
    protected function getRepository()
    {
        return $this->em->getRepository('\\Eko\\MyBundle\\Entity\\Country');
    }
}

We will test a method named findAllOrdered() which must returns countries ordered alphabetically.

Please note that fixtures are loaded from the DataFixtures directory.

Add some fixtures!

Create your fixture file LoadCountryData.php in the directory and add your fixtures content as defined below:


namespace Eko\\MyBundle\\Tests\\Repository\\DataFixtures;

use Doctrine\\Common\\DataFixtures\\AbstractFixture;
use Doctrine\\Common\\Persistence\\ObjectManager;

use Eko\\MyBundle\\Entity\\Country;

/**
 * Loads countries data
 */
class LoadCountryData extends AbstractFixture
{
    /**
     * Load fixtures
     *
     * @param \\Doctrine\\Common\\Persistence\\ObjectManager $manager
     */
    public function load(ObjectManager $manager)
    {
        $manager->clear();
        gc_collect_cycles(); // Could be useful if you have a lot of fixtures

        // France
        $country = new Country();
        $country->setCountryCode('FR');
        $country->setName('France');
        $this->addReference('test-country-fr', $country);

        $manager->persist($country);

        // Italy
        $country = new Country();
        $country->setCountryCode('IT');
        $country->setName('Italy');
        $this->addReference('test-country-it', $country);

        $manager->persist($country);

        // England
        $country = new Country();
        $country->setCountryCode('EN');
        $country->setName('England');
        $this->addReference('test-country-en', $country);

        $manager->persist($country);

        $manager->flush();
    }
}

Nothing really special here, we load 3 entries in our country table (France, Italy, England).

Our fixtures are now loaded in our SQLite test database because they are loaded in our test repository initialization method loadFixturesFromDirectory().

Our repository unit test is now ready to be executed:


$ phpunit -c app src/Eko/MyBundle/Tests/Repository/CountryRepositoryTest.php
PHPUnit 3.7.19 by Sebastian Bergmann.

Configuration read from /vagrant/www/perso/blog/app/phpunit.xml.dist

.

Time: 1 second, Memory: 24.50Mb

OK (1 test, 4 assertions)

I hope that this little test case will help you to make better tests of your Doctrine repositories.

Private: I hope too that EugĂ©nie's health is already a little better too!  

→ More information in the Symfony documentation: http://symfony.com/en/doc/current/cookbook/testing/doctrine.html

Comments