アプリケーションでいくつかの基本的なルートを実行する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をヒットすることを期待しています。
どうすれば解決できますか?
doctrine:fixtures:load
を使用する場合は、--append
オプションを使用して、ユーザーの確認を回避できます。毎回データベースを再作成するため、パージは不要です。私はテストのためにdoctrineフィクスチャのみを使用していましたが、DRYを回避するためにフィクスチャ& LiipFunctionalTestBundle に切り替えました。このバンドルはフィクスチャの管理を容易にします。
編集:David Jacquelの答えは、Doctrine Fixtures:
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);
}
つい最近、バンドル hautelook/AliceBundle は、機能テストでフィクスチャをロードするユースケースの解決に役立つRefreshDatabaseTrait
とReloadDatabaseTrait
の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)
}
}
そして、あなたは良いです!