Behat BDD Some Tricks

Use of assertions for scenario oriented BDD

Very bad idea to do assertions with behat on actions or controllers:
http://programmersgoodies.com/zend-framework-integration-with-behat-bdd

Setting Backgrounds with Persisted Values

https://github.com/knplabs/KnpIpsum-for-symfony/issues/11

  Given I have the following Categories:
        | name              |
        | Animal            |
        | Developer         |
    And I have the following Things:
        | name              | category    |
        | Cat               | Animal      |
        | Dog               | Animal      |

Or perhaps a more generic:

    And I have the following <entityname>s [afresh]:
        | name              | category    |
        | Cat               | Animal      |
        | Dog               | Animal      |

And the entityname and logical name for the repo will be extracted and also wiped out depending on the argument afresh.

<?php
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
$em->getRepository('KnpIpsumBundle:Things')->createQueryBuilder('t')->delete()->getQuery()->execute();
 
foreach ($table->getHash() as $hash) {
    $thing = new Things();
    foreach ($hash as $key => $val) {
        $setter = 'set'.ucfirst($key);
        $thing->$setter($val);
    }
    $em->persist($thing);
}
$em->flush();

Rules for Spec BDD

1) Form sentenses from your tests
2) Form noun with your test cases (test case and test should form a sentense)
3) Describe expectations instead of assertions (this is not currently possible in a clean way with phpspec)

In other words, just make your test cases nouns and tests sentenses, think about what you’re trying to achieve first same as you do with Behat and features but more low level.

This should be our flow: “Talk with your code in pair programming”

1) Write feature
2) Watch it fails
3.1) Write spec (unit-test)
3.2) Watch it fails
3.3) Write code to make spec (unit-test) pass
3.4) Write new spec
3.5) and so one until your FEATURE does pass
4) iterate on 3 over every scenario and feature
5) done

Source: Adapted discussion in channel by stof everzet and me

Now we can do another example besides the calculator done on the repo under the folder Calculator. We will work on a Symfony2 CLI wrapper for ls command.

Objective is to start from a feature describing input/output for CLI console app and to make it pass – we need a wrapper-class in code and symfony command, that will generate UNIX ls command with proper parameters. This is what we are to test with specs phpunit tests.

The spec test would test that the wrapper class generates “ls /home/cordoval” as command, whereas the feature would test the output when running the command on the directory.

So we first start to make the feature:

php app/console --env=test behat @CordovaFormModelBundle/ls.feature
# Features/ls.feature
Feature: listing feature
  In order to know what I have on file
  As an ubuntu beginner
  I want to be told the files and folders on directory
 
  Scenario: listing files and folders
    Given I am in a directory "/home/cordoval/sites-2/FormModelProjectBundle"
    When I run "app/console ls /home/cordoval/sites-2/FormModelProjectBundle"
    Then I should see:
    """
    app
    bin
    deps
    deps.lock
    LICENSE
    README
    README.md
    src
    vendor
    web
    """

The fail state should be:

 Actual output is:
      [InvalidArgumentException]
        Command "ls" is not defined.

And adding this to FeatureContext.php:

/**
     * @Given /^I am in a directory "([^"]*)"$/
     */
    public function iAmInADirectory($dir)
    {
        //if (!file_exists($dir)) {
        //    mkdir($dir);
        //}
        chdir($dir);
    }
 
    /**
     * @When /^I run "([^"]*)"$/
     */
    public function iRun($command)
    {
        exec($command, $output);
        $this->output = trim(implode("\n", $output));
    }
 
    /**
     * @Then /^I should see:$/
     */
    public function iShouldSee(PyStringNode $string)
    {
        if ($string->getRaw() !== $this->output) {
            throw new \Exception(
                "Actual output is:\n" . $this->output
            );
        }
    }
phpunit -c app vendor/bundles/Cordova/Bundle/FormModelBundle/Tests/Command/LsCommandTest.php
class LsCommandTest extends \PHPUnit_Framework_TestCase
{
    // who: "php app/console ls" --> LsCommand.php
    // how: write methods for spec bdd
 
    protected $lsCommand;
 
    protected function setUp()
    {
        $this->lsCommand = new LsCommand();
    }
 
    /**
     * @test
     */
    public function shouldParseWithCordovaColonLs() {
        $this->assertEquals($this->lsCommand->getName(), 'cordova:ls');
    }
 
    /**
     * @test
     */
    public function shouldHaveDescriptionAs() {
        $this->assertEquals($this->lsCommand->getDescription(), 'Lists files and folders');
    }
 
//// and the final class is
 
class LsCommand extends ContainerAwareCommand {
 
    /**
     * @see Command
     */
    protected function configure()
    {
        $this
            ->setName('cordova:ls')
            ->setDescription('Lists files and folders')
            ->setDefinition(array(
                new InputArgument('input', InputArgument::REQUIRED, 'The input directory or file'),
            ))
            ->setHelp(<<<EOT
The <info>cordova:ls</info> command lists files and folders for a given input directory or file
 
  <info>php app/console cordova:ls myProjectDir/subfolder</info>
EOT
            );
    }
 
    /**
     * @see Command
     */
    public function execute(InputInterface $input, OutputInterface $output)
    {
        $dirfile = $input->getArgument('input');
 
        if (null == $dirfile) {
            throw new \InvalidArgumentException('You need to specify a file or directory as argument.');
        }
 
        exec('ls '.$dirfile, $text);
        $output_text = trim(implode("\n", $text));
        $output->writeln($output_text);
    }
 
}
 
// passing test
PHPUnit 3.5.14 by Sebastian Bergmann.
 
..
 
Time: 0 seconds, Memory: 7.75Mb
 
OK (2 tests, 2 assertions)
 
// passing feature
Feature: listing feature
  In order to know what I have on file
  As an ubuntu beginner
  I want to be told the files and folders on directory
 
  Scenario: listing files and folders                                                 # vendor/bundles/Cordova/Bundle/FormModelBundle/Features/ls.feature:8
    Given I am in a directory "/home/cordoval/sites-2/FormModelProjectBundle"         # Cordova\Bundle\FormModelBundle\Features\Context\FeatureContext::iAmInADirectory()
    When I run "app/console cordova:ls /home/cordoval/sites-2/FormModelProjectBundle" # Cordova\Bundle\FormModelBundle\Features\Context\FeatureContext::iRun()
    Then I should see:                                                                # Cordova\Bundle\FormModelBundle\Features\Context\FeatureContext::iShouldSee()
      """
      app
      bin
      deps
      deps.lock
      LICENSE
      README
      README.md
      src
      vendor
      web
      """
 
1 scenario (1 passed)
3 steps (3 passed)
0m0.125s

Another spec bdd feature we can use to track our advancement is this:

$ phpunit --testdox -c app vendor/bundles/Cordova/Bundle/FormModelBundle/Tests/Command/LsCommandTest.php
PHPUnit 3.5.14 by Sebastian Bergmann.
 
Cordova\Bundle\FormModelBundle\Tests\Command\LsCommand
 [x] Should parse with cordova colon ls
 [x] Should have description as

See how nice it gets formatted.

And yet here is another tip to create your skeletons for when you start from the phpunit test or backwards:

phpunit --skeleton-test ClassName
phpunit --skeleton-class TestClassName

As in here: http://www.hashbangcode.com/blog/phpunit-skeleton-classes-588.html

Leave a Reply

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