69

我有一个在我的应用程序中执行一些基本路由的 WebTestCase。

我想,在setUpPHPUnit 的方法上,创建一个与我的主数据库相同的测试数据库,并将夹具加载到其中。

我目前正在做一些解决方法并执行一些控制台命令,如下所示:

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字符来确认操作。

我该如何解决?

4

7 回答 7

38

如果要使用doctrine:fixtures:load,可以使用该--append选项来避免用户确认。由于您每次都在重新创建数据库,因此不需要清除。我曾经单独使用理论夹具进行测试,但后来改用夹具和LiipFunctionalTestBundle来避免 DRY。此捆绑包使灯具更易于管理。

编辑:David Jacquel 的答案是加载 Doctrine Fixtures 的正确答案:

doctrine:fixtures:load --no-interaction 
or
doctrine:fixtures:load -n
于 2013-02-07T15:15:18.077 回答
36

为了绕过用户确认,您可以使用

doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
于 2013-10-15T13:11:25.850 回答
35

更新的答案

您可以为您的测试用例创建一个基类,通过利用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();
    }
}

这样,在运行从上述类继承的每个测试用例之前,数据库模式将从头开始重建,然后在每次测试运行后清理。

希望这可以帮助。

于 2014-03-02T19:53:15.187 回答
6

我偶然发现了一个名为Doctrine-Test-Bundle的非常简洁的包 ,而不是在每个测试上创建和删除模式,它只是回滚。我的测试从 1 分 40 秒到.. 2 秒。而且是孤立的。您所需要的只是一个清晰的测试数据库,它就可以解决问题。

于 2017-06-13T10:15:29.963 回答
2

我使用了这个命令:

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

当然,LiipFunctionalTestBundle 看起来很有希望。

于 2013-11-17T05:55:13.793 回答
1

我想像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);
    }
于 2015-06-21T11:35:09.743 回答
0

就在最近,捆绑包hautelook/AliceBundle公开了两个特征来帮助您解决在功能测试中加载夹具的用例:RefreshDatabaseTraitReloadDatabaseTrait.

从文档:

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

你很好!

于 2018-12-08T13:02:17.597 回答