Найкращий спосіб створити тестову базу даних і навантаження на Symfony 2 WebTestCase?

У мене є 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 , щоб підтвердити дію.

Як я можу це вирішити?

56
Дивіться також мою відповідь тут: stackoverflow.com/questions/17091772/…
додано Автор Marc Juchli, джерело

6 Відповіді

Для того, щоб обійти підтвердження користувача, можна скористатися

doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
34
додано
Величезне спасибі, я шукав цього, не маючи удачі раніше.
додано Автор Guillermo Gutiérrez, джерело
насправді це найкраща відповідь, так як правильно це зробити.
додано Автор GusDeCooL, джерело

If you want to use doctrine:fixtures:load, you can use the --append option to avoid the user confirmation. Since you are recreating the database every time, purging is unnecessary. I used to use doctrine fixtures alone for testing, but have since switched to using fixtures & LiipFunctionalTestBundle to avoid DRY. This bundle makes fixtures easier to manage.

EDIT: Відповідь David Jacquel є правильною для завантаження приладів:

doctrine:fixtures:load --no-interaction 
or
doctrine:fixtures:load -n
31
додано
Одна цікава річ LiipFunctionalTestBundle полягає в тому, що ви можете використовувати власну тестову базу даних, наприклад Rails:/LiipFunctionalTestBundle # sqlite
додано Автор webDEVILopers, джерело
Але є й дискусія про "нову пакунку в місті": додано Автор webDEVILopers, джерело
"Новий пучок в місті" здається трохи завищений. Це питання виповнилося 2 роки, а сам пакет не оновлювався через 10 місяців. Не те щоб Liip був оновлений останнім часом теж.
додано Автор Mike, джерело
Дякуємо за чудову відповідь та посилання на LiipFunctionalTestBundle !
додано Автор Daniel Ribeiro, джерело

ОНОВЛЕНО ВІДПОВІДІ

Ви можете створити базовий клас для тестових випадків, що полегшує завантаження приладу, використовуючи деякі класи з Доктрини/a> бібліотека. Цей клас буде виглядати майже так:

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

СТАРИЙ ВІДПОВІД

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

Є ще більш чистий спосіб досягти цього без виконання команд. В основному це полягає у використанні комбінації 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();
    }
}

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

Сподіваюся, що це допомагає.

@lazel звучить як laravel;)
додано Автор Jaime Sangcap, джерело
рекомендується не очищати вашу базу даних у методі tearDown, якщо ваш тест провалиться, ви все ще можете шукати у вашій базі даних і перевіряти, що сталося не так
додано Автор murtho, джерело
Гарна відповідь! Проте я б запропонував використовувати ознаку замість класу :)
додано Автор Iazel, джерело

Я натрапив на справжній акуратний пакет з назвою Доктрина-тест-пакет Замість того, щоб створювати і скидати схеми на кожному тесті, це просто відкат. Мої тести пройшли від 1 до 40 секунд. І це ізольовано. Все, що вам потрібно, це чітка база даних тестів, і це буде робити трюк.

3
додано

Я використав цю команду:

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

Але, звичайно, LiipFunctionalTestBundle виглядає багатообіцяючим.

2
додано

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

Я розширювався від Symfony WebTestCase і після створення ядра я просто називав свій метод, який працює точно так само, як команда 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
додано