BDD avec behat et Symfony2

behat

Le BDD (Behavior Driven Développement) est une méthode de développement selon laquelle on écrit avec une phrase une fonctionnalité de d’une application pour commencer à l’implémenter. Dans cet article, nous allons voir les étapes de mise en place d’un BDD dans une application Symfony2.

Installation

Commençons par installer behat.

composer require behat/symfony2-extension

Note : Si vous utilisez xdebug, augmentez la valeur de debug.max_nesting_level (256 par exemple)

Configuration

Créez le fichier behat.yml à la racine de votre projet (même niveau à composer.json)

default:
    extensions:
        Behat\Symfony2Extension:
            kernel:
               env: behat
    suites:
        app_suite:
            type: symfony_bundle
            bundle: AppBundle

Lançons la commande

bin/behat –init –suite=app_suite

Essayons de lancer behat

behat

On voit que nous n’avons aucun scénario pour le moment, nous allons donc en définir un. Supposons que l’une des fonctionnalités de notre application est activée seulement lorsque quelqu’un lance la command

« php app/console blog:greeting », le texte « hello world » doit être affiché.

 

Création des Scénarios

A la racine du bundle, créez un dossier Features (même niveau que Controller), et placez les fichiers *.feature

A titre d’exemple, créez un fichier nommé commands.feature dont voici le contenu :

behat_scenario

Ce scénario comporte deux étapes (steps) :

Etape1: When I run « php app/console blog:greeting »
Etape2: Then I should see « hello world »

Lançons behat de nouveau,

behat_feature_context_missing

Là, on nous dit que certaines étapes ne sont pas dans le FeatureContext. On va alors copier le bout de code proposé et le coller dans notre classe FeatureContext (AppBundle\Features\Context\FeatureContext).

Il ne reste plus qu’à compléter leurs implémentations. Dans la méthode iRun par exemple, vous allez expliquer à behat ce que signifie « I run » dans l’étape 1 (when I run « php app/console blog:greeting») ; « php app/console blog:greeting » va être injecté en paramètre de la méthode iRun.

NB : La classe PendingException se trouve dans le namespace Behat\Behat\Tester\Exception au cas où vous l’utilisez.

Ci-dessous, un exemple de classe FeatureContext (FeatureContext.php), on peut y voir une implémentation possible de la méthode iRun et iShouldSee,

<?php

namespace AppBundle\Features\Context;

use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Symfony2Extension\Context\KernelAwareContext;
use Symfony\Component\HttpKernel\KernelInterface;

/**
 * Defines application features from the specific context.
 */
class FeatureContext implements Context, SnippetAcceptingContext, KernelAwareContext
{
    private $zCmdOut = '';
    private $kernel;
    /**
     * Initializes context.
     *niela
     * 
     * Every scenario gets its own context instance.
     * You can also pass arbitrary arguments to the
     * context constructor through behat.yml.
     */
    public function __construct()
    {
    }

    /**
     * @When I run :arg1
     */
    public function iRun($arg1)
    {
        $sfRoot = realpath($this->getContainer()->getParameter('kernel.root_dir').'/..');
        chdir($sfRoot);
        $this->zCmdOut = shell_exec($arg1);
    }

    /**
     * @Then I should see :arg1
     */
    public function iShouldSee($arg1)
    {
        $found = strpos($this->zCmdOut, $arg1) !== false;
        assert($found == true, sprintf("Could not see '%s'", $arg1));
    }

    /**
     * Sets Kernel instance.
     *
     * @param KernelInterface $kernel
     */
    public function setKernel(KernelInterface $kernel)
    {
        $this->kernel = $kernel;
    }

    /**
     * Returns HttpKernel instance.
     *
     * @return KernelInterface
     */
    public function getKernel()
    {
        return $this->kernel;
    }

    /**
     * Returns HttpKernel service container.
     *
     * @return ContainerInterface
     */
    public function getContainer()
    {
        return $this->kernel->getContainer();
    }
}

 

A ce stade, si nous exécutons behat, nous remarquons que le step 2 échoue, car le texte « hello world » n’est pas affiché.

behat_exception

Rappelons qu’il s’agit d’un BDD, comme en TDD, on écrit les tests d’abord, au début ils échouent parce que l’on  a pas encore écrit les implémentations. Ici on a décrit les fonctionnalités, mais pour le moment rien n’a été implémenté, le fail ci-dessus est normal. Cela nous indique que nous somme sur la bonne voie (celle de la BDD).

Développement des fonctionnalités

La prochaine étape est d’éliminer le  fail ci-dessus, en commençant par développer les fonctionnalités. Dans notre exemple il s’agit de créer la command blog:greeting (une commande Symfony2) qui doit afficher « hello world » comme on a spécifié dans les scénarios ci-dessus.

<?php

namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class HelloCommand
 *
 * @author Arson RAHERINIAINA <arson.raheriniaina@netapsys.fr>
 */
class HelloCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this->setName('blog:greeting');
    }
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('hello world');
    }
} 

Maintenant tous les steps passent.

behat_ok

Voilà ! Nous venons de faire un BDD. Dans la pratique, on peut imaginer convertir les demandes de clients, les spécifications fonctionnelles directement en scénarios behat et ne commencer à développer qu’une fois que ces derniers sont en place.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

2 + = huit