43 votes

Comment mettre en place la base de données-lourds des tests unitaires en utilisant Symfony2 PHPUnit?

Je suis assez nouveau dans le monde des tests et je veux m'assurer que je suis sur la bonne voie.

J'essaye de configurer les tests unitaires dans un symfony2 projet à l'aide de phpunit.

PHPUnit est fonctionnel et simple contrôleur par défaut tests, beau travail. Mon projet s'appuie fortement sur la base de données des interactions si, et pour autant que je comprends de phpunit de la documentation, je devrait mettre en place une classe basée sur \PHPUnit_Extensions_Database_TestCase, puis de créer des luminaires pour ma db et de travailler à partir de là.

Pourtant, symfony2 ne propose qu'un WebTestCase de la classe qui ne s'applique qu'à partir d' \PHPUnit_Framework_TestCase hors de la boîte.

Donc suis-je en droit de supposer que je dois créer mon propre DataBaseTestCase qui pour la plupart des copies WebTestCase, la seule différence étant qu'il s'étend de l' \PHPUnit_Extensions_Database_TestCase et met en œuvre toutes ses méthodes abstraites?

Ou est-il une autre "built-in" recommandé flux de travail pour symfony2 concernant la base de données centrée sur les tests?

Comme je veux m'assurer que mes modèles de stocker et de récupérer les bonnes données, je ne veux pas la fin de l'essai sur les spécificités de la doctrine par accident.

35voto

Sgoettschkes Points 5963

Je n'ai jamais utilisé l' PHPUnit_Extensions_Database_TestCase, principalement à cause de ces deux raisons:

  • Il n'est pas à l'échelle. Si vous mettez en place et le démontage de la base de données pour chaque test et que vous avez une application qui s'appuie fortement sur la base de données, vous retrouvez création et suppression le même schéma encore et encore.
  • J'aime avoir mes montages non seulement dans mes tests, mais aussi à l'intérieur de ma base de données de développement et de certains appareils sont même nécessaires à la production (initiale de l'utilisateur administrateur ou catégories de produits ou de quoi que ce soit). De les avoir à l'intérieur d'un xml qui peut seulement être utilisé pour phpunit ne semble pas juste pour moi.

À ma façon, en théorie...

J'utilise la doctrine/doctrine-fixtures-bundle pour les appareils (n'importe quel but) et mettre en place l'ensemble de la base de données avec toutes les fixations. J'ai ensuite exécuter tous les tests à l'encontre de cette base de données et assurez-vous de recréer la base de données si un test a changé.

Les avantages sont que je n'ai pas besoin de configurer une base de données à nouveau si un test ne lit, mais ne changez rien. Pour les changements que j'ai à faire tomber et créer à nouveau, assurez-vous d'annuler les changements.

J'utilise sqlite pour les tests, car je peux mettre en place la base de données, puis copiez le fichier sqlite et de le remplacer par un propre à ramener la base de données d'origine. De cette façon, je n'ai pas de supprimer la base de données, créer et charger tous les appareils de nouveau pour un nettoyage de la base de données.

...et dans le code

J'ai écrit un article à propos de la façon dont je fais de la base de données des tests avec symfony2 et phpunit: http://sgoettschkes.blogspot.com/2012/06/symfony2-test-database-best-pratice.html

Bien qu'il utilise sqlite, je pense que l'on peut facilement faire les changements d'utiliser MySQL ou Postgres ou quoi que ce soit.

Penser plus loin

Voici quelques autres idées qui pourrait fonctionner:

  • J'ai lu une fois sur une configuration de test où avant d'utiliser la base de données de commencer une transaction (à l'intérieur de la méthode de configuration), puis utilisez la destruction de la restauration. De cette façon, vous n'avez pas besoin de configurer la base de données de nouveau et juste besoin d'initialiser une seule fois.
  • Ma configuration décrite ci-dessus présente l'inconvénient que la base de données est mis en place chaque fois que phpunit est exécutée, même si vous ne vous exécutez des tests unitaires avec aucune interaction de base de données. Je suis expérimenter avec une configuration où j'utilise une variable globale qui indique si la base de données a été mis en place et ensuite dans les tests d'appeler une méthode qui vérifie cette variable et initialise la base de données si elle n'est pas encore arrivé. De cette façon, seulement quand un des tests les besoins de la base de données de l'installation qui pourrait arriver.
  • Un problème avec sqlite, c'est qu'il ne fonctionne pas de la même chose que MySQL dans certains cas rares. J'ai eu un problème une fois où quelque chose s'est comporté différentes dans MySQL et sqlite provoquant un test à l'échec quand avec MySQL, tout a fonctionné. Je ne me souviens plus ce que c'était exactement.

-1voto

Munir Points 144

Vous pouvez utiliser cette classe:

<?php

namespace Project\Bundle\Tests;

require_once dirname(__DIR__).'/../../../app/AppKernel.php';

use Doctrine\ORM\Tools\SchemaTool;

abstract class TestCase extends \PHPUnit_Framework_TestCase
{
/**
* @var Symfony\Component\HttpKernel\AppKernel
*/
protected $kernel;

/**
 * @var Doctrine\ORM\EntityManager
 */
protected $entityManager;

/**
 * @var Symfony\Component\DependencyInjection\Container
 */
protected $container;


public function setUp()
{
    // Boot the AppKernel in the test environment and with the debug.
    $this->kernel = new \AppKernel('test', true);
    $this->kernel->boot();

    // Store the container and the entity manager in test case properties
    $this->container = $this->kernel->getContainer();
    $this->entityManager = $this->container->get('doctrine')->getEntityManager();

    // Build the schema for sqlite
    $this->generateSchema();


    parent::setUp();
}

public function tearDown()
{
    // Shutdown the kernel.
    $this->kernel->shutdown();

    parent::tearDown();
}

protected function generateSchema()
{
    // Get the metadatas of the application to create the schema.
    $metadatas = $this->getMetadatas();

    if ( ! empty($metadatas)) {
        // Create SchemaTool
        $tool = new SchemaTool($this->entityManager);
        $tool->createSchema($metadatas);
    } else {
        throw new Doctrine\DBAL\Schema\SchemaException('No Metadata Classes to process.');
    }
}

/**
 * Overwrite this method to get specific metadatas.
 *
 * @return Array
 */
protected function getMetadatas()
{
    return $this->entityManager->getMetadataFactory()->getAllMetadata();
}
}

Et puis vous pouvez tester votre entité. Quelque chose comme ceci (en supposant que vous avez une entité de l'Utilisateur)

//Entity Test
class EntityTest extends TestCase {

    protected $user;

    public function setUp()
    {
         parent::setUp();
         $this->user = new User();
         $this->user->setUsername('username');
         $this->user->setPassword('p4ssw0rd');


         $this->entityManager->persist($this->user);
         $this->entityManager->flush();

    }

    public function testUser(){

         $this->assertEquals($this->user->getUserName(), "username");
         ...

    }

}

Espérons que cette aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X