WordPress loader for functional testing – 04

I’m trying to integrate Codeception and WordPress automated tests to be able to have one more arrow in my WordPress testing quiver and avoid much of the mocking and setup required in a functional WordPress test.

What’s needed to make is systematic?

Looking back at my previous post I managed to have a set up running and will need now to make it systematic to be able to deploy it with a simple composer require command.
I’ve reproduced what I had obtained using the suite _bootstrap.php file in a skeleton Codeception module class and while that works it lacks a certain amount of flexibility.

<?php

namespace Codeception\Module;

use Codeception\Exception\ModuleConfig;
use Codeception\Module;

class WPLoader extends Module
{
  protected $wpBootstrapFile;

    public static $includeInheritedActions = true;
    public static $onlyActions = array();
    public static $excludeActions = array();

    public function _initialize()
    {
        $this->wpBootstrapFile = dirname(__FILE__) . '/includes/bootstrap.php';
        $this->loadWordPress();
    }

    protected function loadWordPress()
    {
        require_once $this->wpBootstrapFile;
    }
}

Configuration anyone?

One of Codeception strengths resides in its configuration possibilities and I want a certain degree of configuration to be possible using the module; I’ve parsed the original automated test suite bootstrap.php and install.php files looking for all the hard-coded and relevant-to-the-tests globally defined constants to come up with an updated and more flexible module

<?php

namespace Codeception\Module;

use Codeception\Exception\ModuleConfig;
use Codeception\Module;

class WPLoader extends Module
{

    /**
     * The fields the user will have to set to legit values for the module to run.
     *
     * wpRootFolder - the absolute path to the root folder of the WordPress
     * installation to use for testing, the ABSPATH global value.
     * dbNAme - the name of the database to use for the tests, will be trashed
     * during tests so take care, will be the DB_NAME global.
     * dbHost - the host the database can be found at, will be the DB_HOST
     * global.
     * dbUser - the database privileged user, should GRANT ALL on the database,
     * will be the DB_USER global.
     * dbPassword - the password for the user, will be the DB_PASSWORD global.
     *
     * @var array
     */
    protected $requiredFields = array(
        'wpRootFolder',
        'dbName',
        'dbHost',
        'dbUser',
        'dbPassword'
    );

    /**
     * The fields the user will be able to override while running tests.
     *
     * All of the fields have a correspondant in the standard `wp-tests-config.php`
     * file found in [WordPress automated testing suite.](http://make.wordpress.org/core/handbook/automated-testing/)
     *
     * wpDebug - bool, def. `true`, the WP_DEBUG global value.
     * multisite - bool, def. `false`, if set to `true` will create a
     * multisite instllation, the WP_TESTS_MULTISITE global value.
     * dbCharset - string, def. `utf8`, the DB_CHARSET global value.
     * dbCollate - string, def. ``, the DB_COLLATE global value.
     * tablePrefix - string, def. `wptests_`, the WP_TESTS_TABLE_PREFIX value.
     * domain - string, def. `example.org`, the root URL of the site, the
     * WP_TESTS_DOMAIN global value.
     * adminEmail - string, def. `admin@example.org`, the admin email, the
     * WP_TEST_EMAIL global value.
     * title - string, def. `Test Blog`, the blog title, the WP_TESTS_TITLE
     * global value.
     * phpBinary - string, def. `php`, the php bin command, the WP_PHP_BINARY
     * global value.
     * language - string, def. ``, the installation language, the WPLANG global
     * value.
     *
     * @var array
     */
    protected $config = array(
        'wpDebug' => true,
        'multisite' => false,
        'dbCharset' => 'utf8',
        'dbCollate' => '',
        'tablePrefix' => 'wptests_',
        'domain' => 'example.org',
        'adminEmail' => 'admin@example.org',
        'title' => 'Test Blog',
        'phpBinary' => 'php',
        'language' => ''
    );

    /**
     * The path to the modified tests bootstrap file.
     *
     * @var string
     */
    protected $wpBootstrapFile;

    public static $includeInheritedActions = true;
    public static $onlyActions = array();
    public static $excludeActions = array();

    /**
     * Defines the globals needed by WordPress to run to user set values.
     *
     * The method replaces the "wp-tests-config.php" file the original
     * testing workflow included to allow run-time customization of the
     * globals in a Codeception friendly way.
     *
     * @return void
     */
    protected function defineGlobals()
    {
        // allow me not to bother with traling slashes
        $wpRootFolder = rtrim($this->config['wpRootFolder'], '/') . '/';
        define('ABSPATH', $wpRootFolder);
        define('DB_NAME', $this->config['dbName']);
        define('DB_USER', $this->config['dbUser']);
        define('DB_PASSWORD', $this->config['dbPassword']);
        define('DB_HOST', $this->config['dbHost']);
        define('DB_CHARSET', $this->config['dbCharset']);
        define('DB_COLLATE', $this->config['dbCollate']);
        define('WP_TESTS_TABLE_PREFIX', $this->config['tablePrefix']);
        define('WP_TESTS_DOMAIN', $this->config['domain']);
        define('WP_TESTS_EMAIL', $this->config['adminEmail']);
        define('WP_TESTS_TITLE', $this->config['title']);
        define('WP_PHP_BINARY', $this->config['phpBinary']);
        define('WPLANG', $this->config['language']);
        define('WP_DEBUG', $this->config['wpDebug']);
        define('WP_TESTS_MULTISITE', $this->config['multisite']);
    }

    /**
     * The function that will initialize the module.
     *
     * @return void
     */
    public function _initialize()
    {

        // check that the wordpress path exists
        if (!file_exists($this->config['wpRootFolder'])) {
            throw new ModuleConfig(__CLASS__, "\nWordpress root folder doesn't exists. Please, check that " . $this->config['wpRootFolder'] . " contains a valid WordPress installation.");
        }

        // WordPress  will deal with database connection errors
        $this->wpBootstrapFile = dirname(__FILE__) . '/includes/bootstrap.php';
        $this->loadWordPress();
    }

    /**
     * Loads WordPress calling the bootstrap file
     *
     * This method does little but wrapping preparing the global space for the
     * original automated testing bootstrap file and taking charge of replacing
     * the original "wp-tests-config.php" file in setting up the globals.
     *
     * @return void
     */
    protected function loadWordPress()
    {
        $this->defineGlobals();

        if ($this->config['multisite']) {
            $this->debug('Running as multisite');
        } else {
            $this->debug('Running as single site');
        }
        require_once $this->wpBootstrapFile;
    }
}

and while it might be long it works and allows me to configure and run the wordpress suite I had originally created adding it to the wordpress.suite.yml file like

class_name: WordpressTester
modules:
    enabled:
        - WordpressHelper
        - WPLoader
    config:
        WPLoader:
            wpRootFolder: '/Users/Luca/websites/php52/route-pages'
            dbName: 'wordpress-tests'
            dbHost: '127.0.0.1'
            dbUser: 'root'
            dbPassword: 'root'

Next

I will finish up the module, no more a proof of concept but something I can work with, and will share it on GitHub adding it to my WP Browser module.