Подтвердить что ты не робот

Практика тестирования базы данных в Symfony2? Как изолировать?

Каковы текущие лучшие методы тестирования взаимодействия с базами данных с Symfony2? У меня простая настройка CRUD, и я хочу убедиться, что мои тесты в порядке. Прямо сейчас у меня есть 4 теста, каждый из которых проверяет, что действия создания, обновления, удаления и списка выполняются нормально.

У меня есть два волшебных метода: __construct и __destruct, на моем тестовом примере. Внутри них я вызываю exec() с помощью "php app/console...", чтобы создать базу данных, создать схему, а затем удалить базу данных. Тем не менее, это SLOW как черт, и это происходит все время, когда у меня более одного теста.

Как мне приступить к тестированию базы данных и изоляции таких тестов?

4b9b3361

Ответ 1

Тестирование базы данных всегда медленное, так как вам нужно создать/удалить свою схему до/после каждого теста. Чтобы избежать ненужных операций, вы можете:

  • создать схему в методе setUpBeforeClass;
  • обеспечить, чтобы ваши 4 теста запускались в одном потоке с аннотацией @depend;
  • в схеме "tearDownAfterClass".

Схема будет создана/удалена только один раз для вашего теста.

Вы также можете установить доктрину для использования базы данных sqlite inmemory (что очень быстро):

doctrine:
    dbal:
        driver: pdo_sqlite
        path: :memory:
        memory: true

В любом случае, "_construct" и "_destruct" никогда не должны использоваться в тестовых случаях phpunit, вместо этого вы должны использовать "setUp" и "tearDown".

Ответ 2

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

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

У меня есть класс базового теста, называемый KernelAwareTest, который помогает мне в построении схемы. Вы можете найти его на gist: https://gist.github.com/1319290

setUp() загружает ядро ​​Symfony и сохраняет ссылку на него в свойстве класса (вместе со ссылками на DIC и диспетчер сущностей). Также выполняется вызов generateSchema() для создания схемы базы данных (в ней используется инструмент Schema Tool из Doctrine).

По умолчанию создается структура базы данных для всех объектов, известных диспетчеру сущности. Вы можете изменить это поведение в своем тестовом классе, переопределив метод getMetadatas().

P.S.: Я попытался использовать в базе данных памяти (sqlite), но это было не идеально. В любом случае, вероятно, лучше запустить тесты против базы данных, которую вы используете при производстве.

Ответ 3

Вопрос довольно старый, но по-прежнему действителен сегодня, поэтому вот мой опыт и то, как я сегодня справляюсь с его проектами в Symfony.

Я начал использовать базу данных SQLite в памяти для своих тестов, и я перестраиваю схему db + вставленные приборы перед каждым отдельным тестовым случаем. Это имело два основных недостатка:

  • Это было слишком медленно: (
  • В dev и prod я использовал Mysql, который вскоре стал проблемой, потому что SQLite просто не имеет всех необходимых функций и иногда ведет себя по-другому.

Использование MSQL для тестирования и перестройка схемы + вставки приборов перед каждым тестом было слишком медленным. Поэтому я искал альтернативы...

Я наткнулся на это сообщение в блоге: http://alexandre-salome.fr/blog/Symfony2-Isolation-Of-Tests

Этот парень предлагает запускать тесты внутри активных транзакций базы данных и просто откатывать любые изменения после каждого отдельного теста.

Я взял эту идею и создал для нее комплект: https://github.com/dmaicher/doctrine-test-bundle

Настройка пакета очень проста и не требует изменения существующих классов тестов php. Внутри он изменяет конфигурацию доктрины для использования пользовательских соединений с базой данных + драйвер.

С помощью этого пакета вы можете просто создать схему базы данных + вставить приборы ONCE BEFORE перед запуском всего testuite (я предпочитаю делать это в пользовательском файле начальной загрузки phpunit). Используя прослушиватель phpunit, все тесты будут выполняться внутри транзакций базы данных.

Я уже давно использую этот пакет, и для меня он отлично работает с использованием SQLite, MySQL или PostgreSQL.

Так как он также используется в проекте symfony-demo.

Ответ 4

тестирование на локальном компьютере - боль в..., поэтому я начал использовать ci system buddy.works(есть бесплатная автономная версия), и для этого я решил решить эту проблему самостоятельно.

результат:

  • все тесты работают
  • проходят тесты по производственным данным sql
  • тесты выполняются в режиме разделения (не в dev или production) - так что я могу сделать все, что я хочу с базой данных
  • все нажатия на git проверяются, и у меня есть уведомление, если что-то сломано
  • все запросы push/pull для развертывания автоматически загружаются в производство

Это мое решение:

  • second parameters.yml в конфигурации с конфигурацией для теста
  • по производству я делаю ежедневный sqldump
  • при запуске теста на ci это резервное копирование sql копируется с помощью scp для тестирования машины
  • чтобы подготовить все это, я использую robo.li(моя конфигурация ниже)

/**
* This is project console commands configuration for Robo task runner.
*
* @see http://robo.li/
*/
class RoboFile extends \Robo\Tasks
{

function  loadDb(){
    $this->taskExecStack()
        ->stopOnFail()
        ->exec(" mysql -h mariadb -u root -pqwerty -e 'create database test' ")
        ->exec(" mysql -h mariadb -u root -pqwerty test < test.sql ")
        ->run();
}


function prepareDb(){
    $this->taskExecStack()
        ->stopOnFail()
        ->exec("cp  app/config/parameters-test.yml app/config/parameters.yml")
        ->run();

    $this->taskReplaceInFile('app/config/parameters.yml')
        ->from('database_host:     127.0.0.1')
        ->to("database_host:     'mariadb'")
        ->run();

    $this->taskReplaceInFile('app/config/parameters.yml')
        ->from('database_user:     dbuser')
        ->to("database_user:     'root'")
        ->run();

    $this->taskReplaceInFile('app/config/parameters.yml')
        ->from('database_password: 123')
        ->to("database_password: 'qwerty'")
        ->run();


}

}

Надеюсь, это поможет вам создать представление о том, как организовать все это. Использование автономного ci сложно настроить, но это действительно хорошая идея