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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Вопрос довольно старый, но по-прежнему актуальный сегодня, поэтому вот мой опыт и то, как я сегодня это делаю в своих проектах 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.

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

результат:

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

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

  1. второй параметр .yml в конфигурации с конфигурацией для теста
  2. на производстве я делаю ежедневно sqldump
  3. при запуске теста на ci эта резервная копия sql копируется с помощью scp на тестовую машину
  4. для подготовки всего этого я использую robo.li (моя конфигурация ниже)

 /** * This is project's 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 сложно настроить, но это действительно хорошая идея