J'ai un WebTestCase qui exécute quelques itinéraires de base dans mon application.
Sur la méthode setUp
de PHPUnit, je souhaite créer une base de données de test identique à la base de données principale et y charger des fixtures.
Je suis en train de contourner le problème et d’exécuter des commandes de console, comme ceci:
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;
}
}
Mais je suis tout à fait sûr que ce n'est pas la meilleure approche, en particulier parce que le doctrine:fixtures:load
s'attend à ce que l'utilisateur frappe un caractère Y
pour confirmer l'action.
Comment puis-je résoudre ça?
Si vous souhaitez utiliser doctrine:fixtures:load
, vous pouvez utiliser l'option --append
pour éviter la confirmation de l'utilisateur. Puisque vous recréez la base de données à chaque fois, la purge est inutile. J'avais l'habitude d'utiliser des appareils de doctrine uniquement pour les tests, mais depuis je suis passé à l'utilisation d'appareils et LiipFunctionalTestBundle pour éviter DRY. Cet ensemble facilite la gestion des appareils.
EDIT: La réponse de David Jacquel est la bonne pour charger les appareils de Doctrine:
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
Pour éviter la confirmation de l'utilisateur, vous pouvez utiliser
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
Vous pouvez créer une classe de base pour vos scénarios de test, ce qui facilite le chargement des appareils en exploitant certaines classes de la bibliothèque Doctrine Data Fixtures . Cette classe ressemblerait beaucoup à ceci:
<?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;
}
}
Ensuite, dans votre cas de test, développez simplement la classe ci-dessus et avant votre test, ajoutez tous les fixtures nécessaires et exécutez-les. Cela purgera automatiquement votre base de données avant de charger les fixtures. Exemple suit:
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!
}
}
(J'ai décidé de "déprécier" cette réponse car elle explique seulement comment nettoyer la base de données sans dire comment charger les fixtures après).
Il existe un moyen encore plus simple de réaliser cela sans avoir à exécuter de commandes. Il consiste essentiellement à utiliser une combinaison de SchemaTool et de ORMPurger. Vous pouvez créer une classe de base abstraite qui effectue ce type d'opérations pour éviter de les répéter pour chaque scénario de test spécialisé. Voici un exemple de code d'une classe de cas de test qui configure une base de données pour un cas de test générique:
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();
}
}
Ainsi, avant d'exécuter chaque cas de test héritant de la classe ci-dessus, le schéma de base de données sera reconstruit à partir de zéro, puis nettoyé après chaque exécution de test.
J'espère que cela t'aides.
Je suis tombé sur un paquet vraiment génial nommé Doctrine-Test-Bundle Au lieu de créer et de supprimer un schéma à chaque test, il ne fait que revenir en arrière. Mes tests sont passés de 1m40s à ... 2s. Et c'est isolé ... Tout ce dont vous avez besoin, c'est d'une base de données de tests claire et ça fera l'affaire.
J'ai utilisé cette commande:
yes | php app/console doctrine:fixtures:load --purge-with-truncate
Mais bien sûr, LiipFunctionalTestBundle semble prometteur.
Je voulais charger tous vos appareils comme le fait la commande doctrine:fixtures:load
. Je ne voulais pas exécuter exec
à partir du scénario de test, car cela semblait être une façon compliquée de faire les choses. J'ai regardé comment la commande de la doctrine le fait elle-même et ai juste copié les lignes pertinentes.
J'ai prolongé Symfony WebTestCase
et après la création du noyau, j'ai simplement appelé ma méthode, qui fonctionne exactement comme la commande 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);
}
Tout récemment, le paquet hautelook/AliceBundle expose deux caractéristiques pour vous aider à résoudre le cas d'utilisation de dispositifs de chargement dans des tests fonctionnels: RefreshDatabaseTrait
et ReloadDatabaseTrait
.
De la doc:
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)
}
}
Et tu es bon!