symfony - principios - test driven development by example
¿La mejor manera de crear una base de datos de prueba y cargar accesorios en Symfony 2 WebTestCase? (6)
RESPUESTA ACTUALIZADA
Puede crear una clase base para sus casos de prueba, lo que facilita la carga de los dispositivos aprovechando algunas clases de la biblioteca de accesorios de datos de Doctrine . Esta clase se vería así:
<?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;
}
}
Luego, en su caso de prueba, simplemente extienda la clase anterior y antes de su prueba agregue todos los accesorios necesarios y ejecútelos. Esto purgará automáticamente su base de datos antes de cargar accesorios. El siguiente ejemplo:
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!
}
}
ANTIGUA RESPUESTA
(Decidí "desaprobar" esta respuesta porque solo explica cómo limpiar la base de datos sin decir cómo cargar los accesorios después).
Hay una forma aún más limpia de lograr esto sin tener que ejecutar comandos. Básicamente consiste en usar una combinación de SchemaTool y ORMPurger. Puede crear una clase base abstracta que realice este tipo de operaciones para evitar repetirlas para cada caso de prueba especializada. Aquí hay un ejemplo de código de una clase de caso de prueba que configura la base de datos para un caso de prueba genérico:
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();
}
}
De esta forma, antes de ejecutar cada caso de prueba que hereda de la clase anterior, el esquema de la base de datos se reconstruirá desde cero y luego se limpiará después de cada ejecución de prueba.
Espero que esto ayude.
Tengo un WebTestCase que ejecuta algunas rutas básicas en mi aplicación.
Quiero, en el método setUp
de PHPUnit, crear una base de datos de prueba idéntica a mi base de datos principal y cargar accesorios en ella.
Actualmente estoy haciendo una solución y estoy ejecutando algunos comandos de consola, algo como esto:
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;
}
}
Pero estoy bastante seguro de que este no es el mejor enfoque, especialmente porque la doctrine:fixtures:load
espera que el usuario Y
clic en Y
char para confirmar la acción.
¿Cómo puedo resolver eso?
Me encontré con un paquete realmente ordenado llamado Doctrine-Test-Bundle En lugar de crear y eliminar el esquema en cada prueba, simplemente se retrotrae. Mis pruebas pasaron de 1m40s a ... 2s. Y está aislado. Todo lo que necesita es una base de datos de prueba clara y hará el truco.
Para omitir la confirmación del usuario puede usar
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
Quería cargar todos sus accesorios como la doctrine:fixtures:load
command does. No quería ejecutar el exec
desde dentro del caso de prueba porque parecía una manera desordenada de hacer las cosas. Miré cómo el comando de doctrina hace esto por sí mismo y simplemente copiado sobre las líneas relevantes.
Me extendí desde Symfony WebTestCase
y después de que se creó Kernel, acabo de llamar a mi método, que funciona exactamente como el comando 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);
}
Si desea usar doctrine:fixtures:load
, puede usar la opción --append
para evitar la confirmación del usuario. Como está recreando la base de datos cada vez, no es necesario purgar. Solía usar accesorios de doctrina solo para probar, pero desde entonces he cambiado a usar accesorios y LiipFunctionalTestBundle para evitar DRY. Este paquete hace que los accesorios sean más fáciles de administrar.
EDITAR: la respuesta de David Jacquel es la correcta para cargar los accesorios Doctrine:
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
Usé este comando:
yes | php app/console doctrine:fixtures:load --purge-with-truncate
Pero, por supuesto, LiipFunctionalTestBundle parece prometedor.