web-dev-qa-db-ja.com

Symfony 2 WebTestCaseにテストデータベースとロードフィクスチャを作成する最良の方法は?

アプリケーションでいくつかの基本的なルートを実行するWebTestCaseがあります。

PHPUnitのsetUpメソッドで、メインデータベースと同じテストデータベースを作成し、それにフィクスチャをロードします。

私は現在、次のような回避策を実行し、いくつかのコンソールコマンドを実行しています。

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 charをヒットすることを期待しています。

どうすれば解決できますか?

62
Daniel Ribeiro

doctrine:fixtures:loadを使用する場合は、--appendオプションを使用して、ユーザーの確認を回避できます。毎回データベースを再作成するため、パージは不要です。私はテストのためにdoctrineフィクスチャのみを使用していましたが、DRYを回避するためにフィクスチャ& LiipFunctionalTestBundle に切り替えました。このバンドルはフィクスチャの管理を容易にします。

編集:David Jacquelの答えは、Doctrine Fixtures:

doctrine:fixtures:load --no-interaction 
or
doctrine:fixtures:load -n
34
Mike

ユーザー確認をバイパスするために、使用できます

doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
35
David Jacquel

更新された回答

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

この方法では、上記のクラスを継承する各テストケースを実行する前に、データベーススキーマがゼロから再構築され、テストの実行ごとにクリーンアップされます。

お役に立てれば。

31
Andrea Sprega

私は Doctrine-Test-Bundle という名前の本当にきちんとしたバンドルに出くわしました。すべてのテストでスキーマを作成および削除する代わりに、単純にロールバックします。私のテストは1分40秒から2秒になりました。そしてそれは孤立しています。必要なのは、明確なテストデータベースのみであり、トリックを実行します。

5
Despirithium

私はこのコマンドを使用しました:

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

しかし、もちろんLiipFunctionalTestBundleは有望に見えます。

2
slashmili

すべてのフィクスチャを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);
    }
0
mickadoo

つい最近、バンドル hautelook/AliceBundle は、機能テストでフィクスチャをロードするユースケースの解決に役立つRefreshDatabaseTraitReloadDatabaseTraitの2つの特性を公開します。

ドキュメントから:

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

そして、あなたは良いです!

0
GiDo