web-dev-qa-db-fra.com

Le meilleur moyen de créer une base de données de test et de charger des appareils sur Symfony 2 WebTestCase?

J'ai un WebTestCase qui exécute quelques itinéraires de base dans mon application.

Sur la méthode setUp de PHPUnit, je souhaite créer une base de données de test identique à la base de données principale et y charger des fixtures.

Je suis en train de contourner le problème et d’exécuter des commandes de console, comme ceci:

class FixturesWebTestCase extends WebTestCase
{
    protected static $application;

    protected function setUp()
    {
        self::runCommand('doctrine:database:create');
        self::runCommand('doctrine:schema:update --force');
        self::runCommand('doctrine:fixtures:load --purge-with-truncate');
    }

    protected static function runCommand($command)
    {
        $command = sprintf('%s --quiet', $command);

        return self::getApplication()->run(new StringInput($command));
    }

    protected static function getApplication()
    {
        if (null === self::$application) {
            $client = static::createClient();

            self::$application = new Application($client->getKernel());
            self::$application->setAutoExit(false);
        }

        return self::$application;
    }
}

Mais je suis tout à fait sûr que ce n'est pas la meilleure approche, en particulier parce que le doctrine:fixtures:load s'attend à ce que l'utilisateur frappe un caractère Y pour confirmer l'action.

Comment puis-je résoudre ça?

61
Daniel Ribeiro

Si vous souhaitez utiliser doctrine:fixtures:load, vous pouvez utiliser l'option --append pour éviter la confirmation de l'utilisateur. Puisque vous recréez la base de données à chaque fois, la purge est inutile. J'avais l'habitude d'utiliser des appareils de doctrine uniquement pour les tests, mais depuis je suis passé à l'utilisation d'appareils et LiipFunctionalTestBundle pour éviter DRY. Cet ensemble facilite la gestion des appareils.

EDIT: La réponse de David Jacquel est la bonne pour charger les appareils de Doctrine:

doctrine:fixtures:load --no-interaction 
or
doctrine:fixtures:load -n
33
Mike

Pour éviter la confirmation de l'utilisateur, vous pouvez utiliser

doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
34
David Jacquel

RÉPONSE MISE À JOUR

Vous pouvez créer une classe de base pour vos scénarios de test, ce qui facilite le chargement des appareils en exploitant certaines classes de la bibliothèque Doctrine Data Fixtures . Cette classe ressemblerait beaucoup à ceci:

<?php

use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

abstract class FixtureAwareTestCase extends KernelTestCase
{
    /**
     * @var ORMExecutor
     */
    private $fixtureExecutor;

    /**
     * @var ContainerAwareLoader
     */
    private $fixtureLoader;

    public function setUp()
    {
        self::bootKernel();
    }

    /**
     * Adds a new fixture to be loaded.
     *
     * @param FixtureInterface $fixture
     */
    protected function addFixture(FixtureInterface $fixture)
    {
        $this->getFixtureLoader()->addFixture($fixture);
    }

    /**
     * Executes all the fixtures that have been loaded so far.
     */
    protected function executeFixtures()
    {
        $this->getFixtureExecutor()->execute($this->getFixtureLoader()->getFixtures());
    }

    /**
     * @return ORMExecutor
     */
    private function getFixtureExecutor()
    {
        if (!$this->fixtureExecutor) {
            /** @var \Doctrine\ORM\EntityManager $entityManager */
            $entityManager = self::$kernel->getContainer()->get('doctrine')->getManager();
            $this->fixtureExecutor = new ORMExecutor($entityManager, new ORMPurger($entityManager));
        }
        return $this->fixtureExecutor;
    }

    /**
     * @return ContainerAwareLoader
     */
    private function getFixtureLoader()
    {
        if (!$this->fixtureLoader) {
            $this->fixtureLoader = new ContainerAwareLoader(self::$kernel->getContainer());
        }
        return $this->fixtureLoader;
    }
}

Ensuite, dans votre cas de test, développez simplement la classe ci-dessus et avant votre test, ajoutez tous les fixtures nécessaires et exécutez-les. Cela purgera automatiquement votre base de données avant de charger les fixtures. Exemple suit:

class MyTestCase extends FixtureAwareTestCase
{
    public function setUp()
    {
        parent::setUp();

        // Base fixture for all tests
        $this->addFixture(new FirstFixture());
        $this->addFixture(new SecondFixture());
        $this->executeFixtures();

        // Fixtures are now loaded in a clean DB. Yay!
    }
}

ANCIENNE RÉPONSE

(J'ai décidé de "déprécier" cette réponse car elle explique seulement comment nettoyer la base de données sans dire comment charger les fixtures après).

Il existe un moyen encore plus simple de réaliser cela sans avoir à exécuter de commandes. Il consiste essentiellement à utiliser une combinaison de SchemaTool et de ORMPurger. Vous pouvez créer une classe de base abstraite qui effectue ce type d'opérations pour éviter de les répéter pour chaque scénario de test spécialisé. Voici un exemple de code d'une classe de cas de test qui configure une base de données pour un cas de test générique:

use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;

abstract class DatabaseAwareWebTestCase extends WebTestCase {

    public static function setUpBeforeClass() {

        parent::setUpBeforeClass();

        $kernel = static::createKernel();
        $kernel->boot();
        $em = $kernel->getContainer()->get('doctrine')->getManager();
        $schemaTool = new SchemaTool($em);
        $metadata = $em->getMetadataFactory()->getAllMetadata();

        // Drop and recreate tables for all entities
        $schemaTool->dropSchema($metadata);
        $schemaTool->createSchema($metadata);
    }

    protected function tearDown() {

        parent::tearDown();

        $purger = new ORMPurger($this->getContainer()->get('doctrine')->getManager());
        $purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
        $purger->purge();
    }
}

Ainsi, avant d'exécuter chaque cas de test héritant de la classe ci-dessus, le schéma de base de données sera reconstruit à partir de zéro, puis nettoyé après chaque exécution de test.

J'espère que cela t'aides.

31
Andrea Sprega

Je suis tombé sur un paquet vraiment génial nommé Doctrine-Test-Bundle Au lieu de créer et de supprimer un schéma à chaque test, il ne fait que revenir en arrière. Mes tests sont passés de 1m40s à ... 2s. Et c'est isolé ... Tout ce dont vous avez besoin, c'est d'une base de données de tests claire et ça fera l'affaire.

5
Despirithium

J'ai utilisé cette commande:

yes | php app/console doctrine:fixtures:load --purge-with-truncate

Mais bien sûr, LiipFunctionalTestBundle semble prometteur.

2
slashmili

Je voulais charger tous vos appareils comme le fait la commande doctrine:fixtures:load. Je ne voulais pas exécuter exec à partir du scénario de test, car cela semblait être une façon compliquée de faire les choses. J'ai regardé comment la commande de la doctrine le fait elle-même et ai juste copié les lignes pertinentes.

J'ai prolongé Symfony WebTestCase et après la création du noyau, j'ai simplement appelé ma méthode, qui fonctionne exactement comme la commande Doctrine load-fixtures.

    /**
     * Load fixtures for all bundles
     *
     * @param Kernel $kernel
     */
    private static function loadFixtures(Kernel $kernel)
    {
        $loader = new DataFixturesLoader($kernel->getContainer());
        $em = $kernel->getContainer()->get('doctrine')->getManager();

        foreach ($kernel->getBundles() as $bundle) {
            $path = $bundle->getPath().'/DataFixtures/ORM';

            if (is_dir($path)) {
                $loader->loadFromDirectory($path);
            }
        }

        $fixtures = $loader->getFixtures();
        if (!$fixtures) {
            throw new InvalidArgumentException('Could not find any fixtures to load in');
        }
        $purger = new ORMPurger($em);
        $executor = new ORMExecutor($em, $purger);
        $executor->execute($fixtures, true);
    }
0
mickadoo

Tout récemment, le paquet hautelook/AliceBundle expose deux caractéristiques pour vous aider à résoudre le cas d'utilisation de dispositifs de chargement dans des tests fonctionnels: RefreshDatabaseTrait et ReloadDatabaseTrait.

De la doc:

namespace App\Tests;

use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class NewsTest extends WebTestCase
{
    use RefreshDatabaseTrait;

    public function postCommentTest()
    {
        $client = static::createClient(); // The transaction starts just after the boot of the Symfony kernel
        $crawler = $client->request('GET', '/my-news');
        $form = $crawler->filter('#post-comment')->form(['new-comment' => 'Symfony is so cool!']);
        $client->submit($form);
        // At the end of this test, the transaction will be rolled back (even if the test fails)
    }
}

Et tu es bon!

0
GiDo