First step into WordPress plugin acceptance testing

It’s months now I’ve been using Codeception for all of my unit testing needs but the necessity never arose, I might have just ignored it, for acceptance testing.
Since WordPress is my actual field of operations it’s there I need acceptance tests to run.

The set up

I will skip the Codeception installation instructions and the local set up of a web development environment and will start from the point where Codeception has been boostrapped with codecept bootstrap and actual test code can be written.

A first acceptance test

After a quick scaffolding of my WordPress installation structure of choice and a quick plugin bootstrapping the first thing I want verify is that the just created plugin will appear among other plugins and will activate and deactivate without errors.
I will create a cept test navigating to the plugin root folder and using codeception cli like

codecept generate:cept acceptance CanActivateAndDeactivatePlugin

Codeception will kindly generate an empty class in the tests/acceptance/CanActivateAndDeactivatePluginCept.php file

<?php 
$I = new AcceptanceTester($scenario);
$I->wantTo('perform actions and see result');

I will modify the code to verify the plugin activates and deactivates without errors. While that might seem trivial and I already know an empty plugin created using my grunt-init template will work I want to lay the foundation for any future test and make sure the plugin will always activate and deactivate without error.

Adding assertions

After some trial and error I came up with the following code

<?php 
$I = new AcceptanceTester($scenario);

$I->am('an administrator user');
$I->wantTo('activate and deactivate the plugin');

// login as administrator
$I->amOnPage('wp-core/wp-login.php');
$I->fillField('Username', 'theAdmin');
$I->fillField('Password', 'thePassword');
$I->click('Log In');    

// go to the plugins page
$I->amOnPage('wp-core/wp-admin/plugins.php');    

// activate the plugin
$I->canSee('Route Pages');
$I->click('Activate', '#route-pages');
$I->see('Plugin activated', '#message.updated');

// deactivate the plugin
$I->click('Deactivate', '#route-pages');
$I->see('Plugin deactivated', '#message.updated');

which revolves around some initial setup of the tests/acceptance.suite.yml

# Codeception Test Suite Configuration

# suite for acceptance tests.
# perform tests in browser using the WebDriver or PhpBrowser.
# If you need both WebDriver and PHPBrowser tests - create a separate suite.

class_name: AcceptanceTester
modules:
    enabled:
        - PhpBrowser
        - AcceptanceHelper
    config:
        PhpBrowser:
            url: 'http://wp-routing.local'

the code will run and work as intended. [caption id=“attachment_1084” align=“aligncenter” width=“1024”]Green! Green![/caption]

Test refactoring

While the code works writing all that boilerplate code again and again, or copying and pasting it for the matter, will increase error probability to a painful level and movig it to a more appropriate location is in order.
Following along the line of Codeception manual I will move the code for the login and plugin pages part to the MemberSteps class.
In terminal

codecept generate:stepobject acceptance Member

and will add the loginAs, loginAsAdmin and amOnPluginsPage methods

// file tests/acceptance/_steps/MemberSteps.php

<?php
namespace AcceptanceTester;

class MemberSteps extends \AcceptanceTester
{
    const ADMIN_USERNAME = 'theAdmin';
    const ADMIN_PASSWORD = 'thePassword';
    const LOGIN_PAGE = 'wp-core/wp-login.php';
    const PLUGINS_PAGE_URL = 'wp-core/wp-admin/plugins.php';

    public function loginAs($username, $password)
    {
        $I = $this;
        $this->amOnPage(self::LOGIN_PAGE);
        $this->fillField('Username', $username);
        $this->fillField('Password', $password);
        $this->click('Log In');
    }

    public function loginAsAdmin()
    {
        $I = $this;
        $I->loginAs(self::ADMIN_USERNAME, self::ADMIN_PASSWORD);
    }

    public function amOnPluginsPage()
    {
        $I = $this;
        $this->amOnPage(self::PLUGINS_PAGE_URL);
    }
}

to have the first acceptance test code refactored to

<?php 
$I = new AcceptanceTester\MemberSteps($scenario);

$I->am('an administrator user');
$I->wantTo('activate and deactivate the plugin');

$I->loginAsAdmin();
$I->amOnPluginsPage();

$I->canSee('Route Pages');
$I->click('Activate', '#route-pages');
$I->see('Plugin activated', '#message.updated');

$I->click('Deactivate', '#route-pages');
$I->see('Plugin deactivated', '#message.updated');

which is cleaner and will allow me to reuse the common parts elsewhere in tests.

The gotcha

The first place I went to add those three methods was the AcceptanceHelper class but that’s not it: the $I variable will not be there and the methods defined in the helpers cannot rely on any Tester method. Helpers should be seen as more basic tests and assertions Tester can build upon while steps should be seen as “batches of Tester methods” or “Tester” methods usually happening together.