Best way to create a test database and load fixtures on Symfony 2 WebTestCase?

后端 未结 7 1434
伪装坚强ぢ
伪装坚强ぢ 2020-12-04 13:11

I have a WebTestCase that executes some basic routes in my application.

I want to, on the setUp method of PHPUnit, create a test database identical to m

相关标签:
7条回答
  • 2020-12-04 13:27

    UPDATED ANSWER

    You can create a base class for your test cases which makes fixture loading easy by leveraging some classes from the Doctrine Data Fixtures library. This class would look pretty much like this:

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

    Then, in your test case, simply extend the above class and before your test add all the needed fixtures and execute them. This will automatically purge your database before loading fixtures. Example follows:

    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

    (I decided to "deprecate" this answer because it only explains how to clean up the database without telling how to load fixtures after).

    There's an even cleaner way of accomplishing this without having to run commands. It basically consists in using a combination of the SchemaTool and the ORMPurger. You can create an abstract base class which performs this kind of operations to avoid repeating them for each specialized test case. Here's a code example of a test case class which sets up database for a generic test case:

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

    This way, before running each test case which inherits from the above class, the database schema will be rebuilt from scratch, then cleaned up after every test run.

    Hope this helps.

    0 讨论(0)
  • 2020-12-04 13:30

    In order to bypass user confirmation you can use

    doctrine:fixtures:load --no-interaction
    or
    doctrine:fixtures:load -n
    
    0 讨论(0)
  • 2020-12-04 13:36

    I wanted to load all your fixtures like the doctrine:fixtures:load command does. I didn't want to run exec from inside the test case because it seemed like a messy way to do things. I looked at how the doctrine command does this itself and just copied over the relevant lines.

    I extended from the Symfony WebTestCase and after the Kernel was created I just called my method which works exactly like the Doctrine load-fixtures command.

        /**
         * 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 讨论(0)
  • 2020-12-04 13:37

    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's answer is the correct one for loading Doctrine Fixtures:

    doctrine:fixtures:load --no-interaction 
    or
    doctrine:fixtures:load -n
    
    0 讨论(0)
  • 2020-12-04 13:43

    I've stumbled upon a really neat bundle named Doctrine-Test-Bundle Instead of creating and dropping schema on every test it simply rollback. My Tests went from 1m40s to.. 2s. And it's isolated. All you need is a clear test database and it'll do the trick.

    0 讨论(0)
  • 2020-12-04 13:43

    I used this command:

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

    But of course LiipFunctionalTestBundle looks promising.

    0 讨论(0)
提交回复
热议问题