Лучший способ создать тестовую базу данных и загрузить приспособления на Symfony 2 WebTestCase?

57

У меня есть WebTestCase, который выполняет некоторые базовые маршруты в моем приложении.

Я хочу, в методе setUp PHPUnit, создать тестовую базу данных, идентичную моей основной базе данных, и загрузить в нее инструменты.

В настоящее время я выполняю некоторые обходные пути и выполняю некоторые команды консоли, что-то вроде этого:

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;
    }
}

Но я уверен, что это не лучший подход, особенно потому, что doctrine:fixtures:load ожидает, что пользователь ударит по Y char, чтобы подтвердить действие.

Как я могу это решить?

Теги:
doctrine
tdd

7 ответов

33
Лучший ответ

Если вы хотите использовать doctrine:fixtures:load, вы можете использовать опцию --append, чтобы избежать подтверждения пользователя. Поскольку вы каждый раз воссоздаете базу данных, очистка не нужна. Раньше я использовал инструменты для доктрины для тестирования, но с тех пор переключился на использование светильников и LiipFunctionalTestBundle, чтобы избежать DRY. Этот комплект упрощает управление светильниками.

РЕДАКТИРОВАТЬ: Ответ Дэвида Жакеля является правильным для загрузки приборов Doctrine:

doctrine:fixtures:load --no-interaction 
or
doctrine:fixtures:load -n
  • 0
    Спасибо за отличный ответ и ссылку на LiipFunctionalTestBundle !
  • 1
    Отличная особенность LiipFunctionalTestBundle заключается в том, что вы можете использовать пользовательскую тестовую базу данных, как это делает Rails: github.com/liip/LiipFunctionalTestBundle#sqlite
Показать ещё 2 комментария
34

Чтобы обойти подтверждение пользователя, вы можете использовать

doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
  • 0
    Большое спасибо, я искал это, без удачи раньше.
  • 0
    на самом деле это лучший ответ, так как правильный способ сделать это.
27

ОБНОВЛЕННЫЙ ОТВЕТ

Вы можете создать базовый класс для своих тестовых примеров, который упрощает загрузку прибора, используя некоторые классы из библиотеки Doctrine Data Fixtures. Этот класс будет выглядеть примерно так:

<?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;
    }
}

Затем, в вашем тестовом примере, просто добавьте вышеприведенный класс и перед тестом добавьте все необходимые приборы и выполните их. Это автоматически очистит вашу базу данных до загрузки светильников. Пример:

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!
    }
}

OLD ANSWER

(я решил "обмануть" этот ответ, потому что он объясняет, как очистить базу данных, не указав, как после этого нужно загрузить оснастки).

Существует еще более чистый способ выполнить это без необходимости запуска команд. В основном это состоит в использовании комбинации SchemaTool и ORMPurger. Вы можете создать абстрактный базовый класс, который выполняет такие операции, чтобы избежать повторения их для каждого специализированного тестового примера. Вот пример кода класса теста, который устанавливает базу данных для общего тестового примера:

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();
    }
}

Таким образом, перед запуском каждого тестового примера, который наследуется от вышеуказанного класса, схема базы данных будет восстановлена ​​с нуля, а затем очищена после каждого тестового прогона.

Надеюсь, что это поможет.

  • 1
    Хороший ответ! Однако я бы предложил использовать черту вместо класса :)
  • 0
    @lazel звучит как Laravel;)
Показать ещё 1 комментарий
5

Я наткнулся на действительно аккуратный пакет под названием Doctrine-Test-Bundle. Вместо создания и удаления схемы при каждом тесте это просто откат. Мои тесты прошли с 1м40 до.. 2с. И это изолированные. Все, что вам нужно, это чистая тестовая база данных, и она сделает свое дело.

2

Я использовал эту команду:

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

Но, конечно, LiipFunctionalTestBundle выглядит многообещающим.

0

Совсем недавно пакет hautelook/AliceBundle раскрыл две особенности, которые помогут вам решить вариант использования загрузки приборов в функциональных тестах: RefreshDatabaseTrait и ReloadDatabaseTrait.

Из документа:

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)
    }
}

И ты хороший!

0

Я хотел загрузить все ваши светильники, как это делает команда doctrine:fixtures:load. Я не хотел запускать exec изнутри тестового примера, потому что это казалось беспорядочным способом делать что-то. Я посмотрел, как команда doctrine делает это сама и просто копируется по соответствующим строкам.

Я расширился от Symfony WebTestCase и после того, как ядро ​​было создано, я просто вызвал свой метод, который работает точно так же, как команда Load-fictures Doctrine.

    /**
     * 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);
    }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню