Generating Gherkin step definitions from Codeception modules 01

Introduction to the generation of Gherkin step definitions from Codeception modules using the Steppify command.

Not just WordPress

While in the example below I’m using WordPress specific modules from the wp-browser package the steppify command will work with any Codeception module.

Setup

Before any acceptance test runs here is the project setup:

  • a web-server to serve a WordPress installation at http://wp.dev
  • a wp database to host the WordPress installation
  • a Composer configuration file to require lucatume/wp-browser package and the lucatume/codeception-steppify command:
    {
    "name": "lucatume/steppify-test",
    "description": "Steppify Codeception command test",
    "type": "project",
    "require-dev": {
        "lucatume/wp-browser": "^1.19.5",
        "lucatume/codeception-steppify": "dev-master"
    },
    "license": "GPL-2.0",
    "authors": [
        {
            "name": "Luca Tumedei",
            "email": "[email protected]"
        }
    ],
    "minimum-stability": "stable",
    "require": {}
    }
    
  • Codeception configuration file set to use the gherkin:steppify command:
    actor: Tester
    paths:
        tests: tests
        log: tests/_output
        data: tests/_data
        helpers: tests/_support
    settings:
        bootstrap: _bootstrap.php
        colors: true
        memory_limit: 1024M
    extensions:
        commands:
            - tad\Codeception\Command\Steppify
    
    
  • the acceptance suite configuration file set to use the WPBrowser and WPDb modules:
    class_name: AcceptanceTester
    modules:
        enabled:
            - \Helper\Acceptance
            - WPDb
            - WPBrowser
        config:
            WPDb:
                dsn: 'mysql:host=localhost;dbname=wp'
                user: root
                password: root
                dump: tests/_data/dump.sql
                populate: true
                cleanup: true
                url: 'http://wp.dev'
                tablePrefix: wp_
            WPBrowser:
                url: 'http://wp.dev'
                adminUsername: admin
                adminPassword: admin
                adminPath: /wp-admin
    

Note that that tests/_data/dump.sql file is the result of the wp-cli one-liner:

wp site empty --yes && wp db export wp-content/plugins/steppify/tests/_data/dump.sql

All this in place it’s time to write the first feature file tapping into Codeception Gherkin support after a first codecept build run.

Home feature

I’ve written the first Gherkin feature below:

Feature: the homepage displays a list of the latest posts

    Scenario: the homepage will show the latest posts
        Given I have post in database:
            | post_title | post_type |
            | Post 1     | post      |
            | Post 2     | post      |
            | Post 3     | post      |
        When I am on page '/'
        Then I see number of elements 

Following Codeception Gherkin support and flow I would normally run the feature and see the step definitions are not implemented:

codecept run tests/acceptance/home.feature

not-implemented

Codeception provides help along the way generating code snippets to implement the missing step definitions for me that I will copy and paste in my AcceptanceTester class:

codecept g:snippets acceptance

snippets

The text of the steps lends itself to an easy implementation using methods provided by WPDb and WPBrowser modules:

<?php
use Behat\Gherkin\Node\TableNode;

class AcceptanceTester extends \Codeception\Actor
{
    use _generated\AcceptanceTesterActions;

    /**
     * @Given I have post in database
     */
    public function iHavePostInDatabase(TableNode $posts)
    {
        $rows = $posts->getRows();
        $keys = array_shift($rows);

        foreach ($rows as $row) {
            $this->havePostInDatabase(array_combine($keys, $row));
        }
    }

    /**
     * @When I am on page :page
     */
    public function iAmOnPage($page)
    {
        $this->amOnPage($page);
    }

    /**
     * @Then I see number of elements :selector and expected :expected
     */
    public function iSeeNumberOfElementsAndExpected($selector, $expected)
    {
        $this->seeNumberOfElements($selector, $expected);
    }
}

And running the feature will yield a green light:

steps-implementation

Not reinventing the wheel

While the flow and implementation above works it’s not ideal: if the person writing the features is not the same implementing the steps definitions then there will always be a “stop-and-go” approach.
The person writing the features should be provided an initial dictionary of implemented and ready to use steps to tap into IDEs autocompletion and avoid duplicates and redundancies.
In the implementation above I’m merely wrapping methods provided by the WPDb and WPBrowser modules with little or no argument juggling to make them “fit”; it’s easy to see that a scenario step like:

I login as admin

will have an implementation that is, once again, a wrapping around the WPBrowser::loginAsAdmin method:

/**
 * @Given I login as admin
 */
public function iLoginAsAdmin()
{
    $this->loginAsAdmin();
}

The Codeception modules into step definitions.

Steppifying the modules

Running the command itself is easy:

codecept g:steppify "WPDb"
codecept g:steppify "WPBrowser"

steppify

The command created two new files in the tests/_support/_generated folder: WPBrowserGherkinSteps.php and WPDbGherkinSteps.php.
The two files define two traits that I will now import in the AcceptanceTester class:

<?php
use _generated\WPBrowserGherkinSteps;
use _generated\WPDbGherkinSteps;

class AcceptanceTester extends \Codeception\Actor
{
    use _generated\AcceptanceTesterActions;
    use WPBrowserGherkinSteps;
    use WPDbGherkinSteps;
}

Running the feature again yields the following:

implemented-trait

The feature did pass again so it’s time to see how methods from the WPDb and WPBrowser have been translated; I will higlight just the methods I’m interested in but the command “steppified” all of them.
The WPBrowser::amOnPage method was translated in the step definition below:

/**
 * [!] Method is generated from steppify task. Documentation taken from corresponding module.
 *
 * @Given I am on page :page
 * @When I am on page :page
 * @Then I am on page :page
 *
 * @see \Codeception\Module\WPBrowser::amOnPage()
 */
public function step_amOnPage($page) {
    $args = func_get_args();
    return $this->getScenario()->runStep(new \Codeception\Step\Action('amOnPage', $args));
}

It’s really not that different from the one I’ve written by hand and, following the command default behaviour, will generate step definitions of all types: given, when and then.
Behat will not really care but it’s easier to skim for humans.
The WPDb::havePostInDatabase method generated this step definition:

/**
 * [!] Method is generated from steppify task. Documentation taken from corresponding module.
 *
 * @Given I have post in database
 * @When I have post in database
 * @Then I have post in database
 *
 * @see \Codeception\Module\WPDb::havePostInDatabase()
 */
public function step_havePostInDatabase(\Behat\Gherkin\Node\TableNode $data) {
    $args = steppify_convertTableNodesToArrays(func_get_args(), $iterations);

    if(!empty($iterations)) {
        $returnValues = [];
        foreach($iterations as $iteration){
            $returnValues[] = $this->getScenario()->runStep(new \Codeception\Step\Action('havePostInDatabase', $iteration));
        }

        return $returnValues;
    }

    return $this->getScenario()->runStep(new \Codeception\Step\Action('havePostInDatabase', $args));
}

Where the original method would have the signature

public function havePostInDatabase(array $data = []);

the step definition has converted that array type hinting into a TableNode and the command did take care to add a conversion step to convert the table into an array through the steppify_convertTableNodesToArrays function.
Finally the WPBrowser::seeNumberOfElements method has been translated into the step definition below:

/**
 * [!] Method is generated from steppify task. Documentation taken from corresponding module.
 *
 * @Given I see number of elements :selector and expected :expected
 * @When I see number of elements :selector and expected :expected
 * @Then I see number of elements :selector and expected :expected
 *
 * @see \Codeception\Module\WPBrowser::seeNumberOfElements()
 */
public function step_seeNumberOfElements($selector, $expected) {
    $args = func_get_args();
    return $this->getScenario()->runStep(new \Codeception\Step\Action('seeNumberOfElements', $args));
}

While the conversion is fine out of the box and it works some tweaking could make the steps read exactly as I want.

Next

I will dig a little into the command configuration possibilities to tweak and tailor the step definition generation to my taste.

I appreciate your input