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 thelucatume/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": "luca@theaveragedev.com" } ], "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 theWPBrowser
andWPDb
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
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
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:
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 steppify
command makes kick-starting a step definition dictionary easier translating existing methods provided by Codeception modules into step definitions.
Steppifying the modules
Running the command itself is easy:
codecept g:steppify "WPDb"
codecept g:steppify "WPBrowser"
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:
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.