Лучший способ создать тестовую базу данных и загрузить приспособления на 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
символ для подтверждения действия.
Как я могу решить это?
6 ответов
Если вы хотите использовать doctrine:fixtures:load
, вы можете использовать --append
возможность избежать подтверждения пользователя. Поскольку вы воссоздаете базу данных каждый раз, очистка не требуется. Раньше я использовал только приборы для доктрины для тестирования, но с тех пор перешел на использование приборов и https://github.com/liip/LiipFunctionalTestBundle, чтобы избежать DRY. Этот комплект облегчает управление приборами.
РЕДАКТИРОВАТЬ: ответ Дэвида Жакеля является правильным для загрузки учений доктрины:
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
Чтобы обойти подтверждение пользователя, вы можете использовать
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
ОБНОВЛЕННЫЙ ОТВЕТ
Вы можете создать базовый класс для ваших тестовых случаев, который облегчает загрузку приборов, используя некоторые классы из библиотеки 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!
}
}
СТАРЫЙ ОТВЕТ
(Я решил "отказаться" от этого ответа, потому что он объясняет только, как очистить базу данных, не сообщая, как загружать данные после).
Есть еще более чистый способ сделать это без необходимости запуска команд. Он в основном состоит в использовании комбинации 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();
}
}
Таким образом, перед выполнением каждого тестового примера, который наследуется из вышеуказанного класса, схема базы данных будет перестраиваться с нуля, а затем очищаться после каждого запуска теста.
Надеюсь это поможет.
Я наткнулся на действительно аккуратный пакет под названием Doctrine-Test-Bundle. Вместо создания и удаления схемы при каждом тесте это просто откат. Мои тесты прошли с 1м40 до.. 2с. И он изолирован. Все, что вам нужно, это чистая тестовая база данных, и она сделает свое дело.
Я использовал эту команду:
yes | php app/console doctrine:fixtures:load --purge-with-truncate
Но, конечно, LiipFunctionalTestBundle выглядит многообещающе.
Я хотел загрузить все ваши приборы, такие как doctrine:fixtures:load
команда делает. Я не хотел бежать exec
изнутри тестового случая, потому что это казалось грязным способом делать вещи. Я посмотрел на то, как команда doctrine делает это сама, и просто скопировал соответствующие строки.
Я продлил с 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);
}
Совсем недавно пакет https://github.com/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)
}
}
И ты хороший!