Close
Behat to the Fixtures

Comment utiliser behat pour écrire des fixtures ?

Commençons par un petit rappel de ce qu’est une fixture selon la documentation de DoctrineFixturesBundle :

Fixtures are used to load a controlled set of data into a database. This data can be used for testing or could be the initial data required for the application to run smoothly. Symfony2 has no built in way to manage fixtures but Doctrine2 has a library to help you write fixtures.

Le but de cet article est de présenter une nouvelle méthode, basée sur le langage Gherkin et Behat 3, pour écrire et charger des fixtures dans Symfony2.

Quelle est la façon actuelle d’écrire des fixtures avec Symfony ?

Il y a deux façons principales d’écrire des fixtures dans Symfony :

class LoadUserAndBookData implements FixtureInterface {
     public function load(ObjectManager $manager) {
          $authorJKR = new Author();
          $authorJKR->setName('JK Rowling');
          $manager->persist($authorJKR);

          $authorDB = new Author();
          $authorDB->setName('Dan Brown');
          $manager->persist($authorDB); 
 
          $book1 = new Book();
          $book1->setTitle('Harry Potter');
          $book1->setAuthor($authorJKR); 
          $manager->persist($book1);
 
          $book2 = new Book();
          $book2->setTitle('Da Vinci Code');
          $book2->setAuthor($authorDB); 
          $manager->persist($book2);
 
          $manager->flush();
     }
}

Ou bien d’utiliser un format yml (avec hautelook/alice-bundle) :

AppBundle\Entity\Author:
     author1:
          name: JK Rowling
     author2:
          name: Dan Brown

AppBundle\Entity\Book:
     book1:
          title: Harry Potter
          author: @author1
     book2:
          title: Da Vinci Code
          author: @author2

Des fixtures en langage Gherkin

Je propose ici une troisième façon d’écrire des fixtures : en language Gherkin.

Scenario: fixture 1
    Given authors:
        | name       |
        | JK Rowling |
        | Dan Brown  |
    Given books:
        | title         | author     |
        | Harry Potter  | JK Rowling |
        | Da Vinci Code | Dan Brown  |

Le langage Gherkin est moins verbeux que le format yml, ce qui rend la fixture plus lisible. Même si vous n’êtes pas un développeur, vous pouvez écrire des fixtures sous ce format-là.

Comment interpréter le Gherkin ?

Si Behat est déjà utilisé sur le projet pour faire les tests, tous les « Given » doivent déjà être définis dans un des Context. Par exemple, voici le code correspondant à la fixture ci-dessus :

/**
 * @Given authors:
 */
public function givenAuthors(TableNode $tableAuthors)
{
    foreach ($tableAuthors as $authorRow) {
        $author = new Author();
        $author->setName($authorRow['name']);
        $this->em->persist($author);
    }
    $this->em->flush();
}

/**
 * @Given books:
 */
public function givenBooks(TableNode $tableBooks)
{
    foreach ($tableBooks as $bookRow) {
        $book = new Book();
        $book->setTitle($bookRow['title']);
        $book->setAuthor($this->findAuthorByName($bookRow['author']));
        $this->em->persist($book);
    }
    $this->em->flush();
}

Comment paramétrer behat 3 ?

Les tests Behat utilisent une base de données de test. Mais les fixtures doivent utiliser la base de données de dev. Pour cela, il faut créer le fichier « behat_fixtures.yml » à la racine du projet avec le contenu suivant :

default:
    extensions:
        Behat\Symfony2Extension:
            kernel:
                env: dev
    suites:
        fixtures:
            type: symfony_bundle
            bundle: 'AppBundle'
            paths:
                - src/AppBundle/DataFixtures/
            contexts:
                - AppBundle\Features\Context\FixturesContext
                - ...

Tout d’abord, il faut définir l’environnement qui doit être utilisé : « dev » (« par défaut c’est « test »)

default:
    extensions:
        Behat\Symfony2Extension:
            kernel:
                env: dev

Ensuite, il faut spécifier le dossier qui contient les fixtures, ici : « src/AppBundle/DataFixtures/ »

default:
    suites:
        fixtures:
            paths:
                - src/AppBundle/DataFixtures/

La liste des class Context est la même que pour les tests Behat, il faut simplement ajouter à cette liste FixturesContext (voir ci-dessous).

Comment gérer la base de données avant de charger les fixtures ?

Voici le contenu du fichier « FixturesContext », son but est de gérer la base de données.

class FixturesContext implements Context
{
    /** @BeforeSuite */
    public static function before($event)
    {
        $kernel = new AppKernel("dev", true);
        $kernel->boot();
    
        FixturesContext::checkDatabasesHosts($kernel);
    
        $application = new Application($kernel);
        $application->setAutoExit(false);
        FixturesContext::runConsole($application, 'doctrine:schema:drop', ['--force' => true, '--full-database' => true]);
        FixturesContext::runConsole($application, 'doctrine:schema:create');
        $kernel->shutdown();
    }
    
    private static function runConsole($application, $command, $options = [])
    {
        $options['-e'] = 'dev';
        $options['-q'] = null;
        $options = array_merge($options, ['command' => $command]);
        
        return $application->run(new ArrayInput($options));
    }
}

Tout d’abord, il faut s’assurer que le host de la base de données est bien « localhost » (sur une vagrant par exemple), afin d’éviter de manipuler la mauvaise base.

Ensuite, il faut supprimer la base de données existante, puis la recréer grâce aux entités doctrine. Il est également possible d’utiliser un script de migration tel que DoctrineMigration.

L’écriture des fixtures

Une fois que behat est bien paramétré et que le fichier FixturesContext fonctionne, il faut s’attaquer à l’écriture des fixtures en langage Gherkin. Par exemple :

Feature: fixtures library

    Scenario: fixture writers
        Given authors:
            | name             | birth date |
            | JK Rowling       | 1965-07-31 |
            | Dan Brown        |            |
            | J. R. R. Tolkien | 1892-01-03 |

    Scenario: fixture books
        Given literary genres:
            | name            |
            | science fiction |
            | comedy          |
        Given books:
            | title             | author     | literary genres | publication year |
            | Harry Potter      | JK Rowling | science fiction | 1954             |
            | Da Vinci Code     | Dan Brown  |                 | 2003             |
            | Angels and Demons | Dan Brown  |                 | 2000             |

Les fichiers de fixture (*.feature) doivent être placés dans le dossier défini dans « behat_fixtures.yml » (ici : src/AppBundle/DataFixtures/).

Behat traite les fichiers par ordre alphabétique. Pour contrôler l’ordre des fixtures, il suffit de préfixer les noms des fichiers avec un numéro.

Comment lancer le chargement des fixtures ?

Pour charger les fixtures, il faut exécuter le script suivant à la racine du projet. Il suffit ensuite d’attendre quelques secondes !

bin/behat --config behat_fixtures.yml

Conclusion

Il y a beaucoup d’avantages à utiliser behat 3 pour charger des fixtures :

  • Les fixtures sont plus lisibles et faciles à écrire.
  • Si vous utilisez déjà behat dans votre projet, les définitions des fichiers Context peuvent être réutilisées.
  • Si vous ajoutez une colonne en base de données, il suffit de modifier le code à un seul endroit pour faire évoluer les tests et les fixtures en même temps.
  • Il est très facile de définir des valeurs par défaut (par exemple, le mot de passe d’un utilisateur peut être « pwd » par défaut).

Selon moi, le seul point négatif est le temps d’exécution. En effet, celui-ci est généralement plus long que les autres méthodes utilisées dans Symfony.

Si vous aimez Behat, vous allez adorer écrire des fixtures avec ! 😉

Leave a Reply

Your email address will not be published. Required fields are marked *