WordPress Plugin Unit Testing


February 24, 2015 Maithilish

Chapter 12. WordPress Plugin Unit Testing

One of the challenges that we faced while developing the Share on Social Plugin was to setup unit tests. Many tutorials and blogs on the subject and even the official WordPress documentation couldn’t help us much to overcome the initial hurdles. Knowing well the difficulties faced by the beginners, some developers have packaged and hosted the testing frameworks for WordPress on GitHub to ease the setup but they are unable to reduce the overall confusion.
For us, it took some time to figure out how WordPress itself tests its core. Once we understood the WordPress testing setup and methodology, plugin tests became quite smooth. This tutorial series – WordPress Plugin Unit Testing – is based on our experience in testing a production ready plugin, and it explains testing in a step by step fashion so that beginners can easily setup unit test for their projects. The blogs in this cookbook covers important aspects of plugin unit test; setup and configure WordPress Testing Library, plugin basic and advanced tests such as tests for action and filter, i18n, Ajax and Multisite.
We will try to make the tutorial as easy as possible considering the size of test suit.. While there are about 2000 plus lines of code in our plugin, the code to test them crosses 5000 lines. It would be quite boring to explain all the tests, file by file, as some test patterns repeats throughout the test suit. Instead, we group the test cases into categories and take a couple of typical test cases from each category and explain their pattern so that you can easily adapt them for your project.
However, we will not dwell too much on unit test per se – it’s importance and the best practices – as there are enough blogs on the subject and so, we avoid repeating those things all over again.

12.1. WordPress PHPUnit Setup

In this tutorial, we install the modules and then do the configurations required to run Plugin unit tests. Most confusing part of WordPress testing is its configuration and keeping this in mind, tutorial provides step by step instructions with an example of Simple WordPress Plugin. Once you understand the configuration and able to run tests against Simple Plugin, it is easy to map it for any plugin you plan to develop.

Install PHPUnit

WordPress uses PHPUnit unit test framework for PHP. The latest release of PHPUnit is bundled as a PHAR file (PHP Archive) which is quite easy to install and use.
For Linux, install PHPUnit with following commands.

$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar
$ mv phpunit.phar /usr/local/bin/phpunit

Verify the installation, with following command.

$ phpunit --version
PHPUnit x.y.z by Sebastian Bergmann and contributors.

For Windows, refer PHPUnit Installation for Windows. PHPUnit requires PHP 5.3.3 or above, and Windows users also have to install PHP, if not installed.

Create Simple Plugin

To understand the unit test setup, we require a simple plugin. If you have already created the simple plugin explained in Section 2.1, “WordPress Simple Plugin” then you can reuse it, otherwise create a new one as explained here.
Create a directory named wp-simple-plugin in some location and add plugin main file wp-simple-plugin.php to it with following contents.
wp-simple-plugin/wp-simple-plugin.php

<?php

/**
 * Plugin Name: WordPress Simple Plugin
 * Plugin URI: http://www.codetab.org/wordpress-tutorial/
 * Description: Plugin to explain WordPress Plugin Basics
 * Version: 1.0.0
 * Author: maithilish
 * Author URI: http://www.codetab.org/about/
 * License: GPLv2
 */

defined( 'ABSPATH' ) or die( "Access denied !" );

define('WPSP_NAME','wp-simple-plugin');

define( "WPSP_URL", trailingslashit( plugin_dir_url( __FILE__ ) ) );

As we not going to install or use the simple plugin in WordPress, there is nothing functional in it apart from the usual plugin header and a couple of define statements.
Next, we require a directory to hold configuration files and the test cases. Create following directories under wp-simple-plugin folder with following commands.

$ cd wp-simple-plugin
$ mkdir tests
$ mkdir tests/phpunit
$ mkdir tests/phpunit/tests

// or create all directories recursively

$ mkdir -p tests/phpunit/tests  

Install WordPress Tests Library

To run unit test against WordPress, we require WordPress Tests Library. Install the library under tests/phpunit directory with following commands.

$ cd wp-simple-plugin
$ cd tests/phpunit
$ svn co https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/

WordPress PHPUnit Setup - WordPress Test Library files
SVN pulls the latest version of WordPress Test Library from WordPress repository and saves it under tests/phpunit/includes .
When we run tests, WordPress Test Library ( i.e. files of includes directory ) integrates PHPUnit with WordPress to handle following tasks.
  • creates a new instance of WordPress.
  • creates WP tables in test database.
  • runs test cases against test instance of WP including special test cases such as Ajax calls and Multisite tests cases.
  • when tests are through, deletes the tables to clean up the test database.

Setup Configuration Files

The first configuration file is phpunit.xml, which as name suggests is a PHPUnit file and it defines the test suit and test files. Add phpunit.xml with following contents to the top directory of the plugin.
wp-simple-plugin/phpunit.xml

<phpunit bootstrap="tests/phpunit/bootstrap.php" backupGlobals="false"
        colors="false" convertErrorsToExceptions="true"
        convertNoticesToExceptions="true" convertWarningsToExceptions="true">
        <testsuites>
                <testsuite>
                        <file>tests/phpunit/tests/test-wp-simple-plugin.php</file>
                </testsuite>
        </testsuites>
</phpunit>

PHPUnit configuration defines single testsuite with one test file. Later we add more test files to the testsuit element.
In phpunit element we indicate that PHPUnit has to use tests/phpunit/bootstrap.php to bootstrap the tests. Add the bootstrap.php to wp-simple-plugin/tests/phpunit directory.
wp-simple-plugin/tests/phpunit/bootstrap.php

<?php

// path to test lib bootstrap.php
$test_lib_bootstrap_file = dirname( __FILE__ ) . '/includes/bootstrap.php';

if ( ! file_exists( $test_lib_bootstrap_file ) ) {
    echo PHP_EOL . "Error : unable to find " . $test_lib_bootstrap_file . PHP_EOL;
    exit( '' . PHP_EOL );
}

// set plugin and options for activation
$GLOBALS[ 'wp_tests_options' ] = array(
        'active_plugins' => array(
                'wp-simple-plugin/wp-simple-plugin.php'
        ),
        'wpsp_test' => true
);

// call test-lib's bootstrap.php
require_once $test_lib_bootstrap_file;

$current_user = new WP_User( 1 );
$current_user->set_role( 'administrator' );

echo PHP_EOL;
echo 'Using WordPress core : ' . ABSPATH . PHP_EOL;
echo PHP_EOL;

In bootstrap.php, we first check whether WordPress Tests Libs’ bootstrap.php exists in includes directory else exit from the test run. Next, we create an array which holds details of Simple Plugin as active plugins and add the array to $GLOBALS[‘wp_tests_options’]. WordPress Tests Lib activates and loads the plugin using these details. We will explain the significance of the second item of the array, wpsp_test, in the next blog.
After defining the plugin details, we call includes/bootstrap.php using require_once statement. The includes/bootstrap.php which is actually the bootstrap file that comes with WordPress Tests Lib loads new WordPress instance, activates and loads the Simple Plugin.
Next, we create the default user for test by using WordPress class WP_User and set administrator role to it.
Earlier, we explained that includes/bootstrap.php creates a new instance of WordPress, but from where it gets the configurations for WordPress. The third and last configuration file is the wp-tests-config.php which provides the configurations to WordPress test instance. Add the wp-tests-config.php to tests/phpunit directory with following contents.
wp-simple-plugin/tests/phpunit/wp-tests-config.php

<?php

// change the next line to points to your wordpress dir
define( 'ABSPATH', '/opt/lampp/wptest/wordpress/' );

define( 'WP_DEBUG', false );

// WARNING WARNING WARNING!
// tests DROPS ALL TABLES in the database. DO NOT use a production database

define( 'DB_NAME', 'wptest' );
define( 'DB_USER', 'wptest' );
define( 'DB_PASSWORD', 'wptest' );
define( 'DB_HOST', 'localhost.localdomain' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );

$table_prefix = 'wptests_'; // Only numbers, letters, and underscores please!

define( 'WP_TESTS_DOMAIN', 'localhost' );
define( 'WP_TESTS_EMAIL', 'admin@example.org' );
define( 'WP_TESTS_TITLE', 'Test Blog' );

define( 'WP_PHP_BINARY', 'php' );

define( 'WPLANG', '' );

It defines the location of WordPress software using which instance has to be created and the name of the test database.
At this point, your wp-simple-plugin directory structure should be similar to the one shown in the screenshot.
WordPress Plugin Unit Testing - unit test directory structure

Create Test Database

In wp-tests-config.php we defined MySQL connection properties pointing to a test database. We need to create the database and user, and grant permissions so that WordPress can create tables and run tests. Use following commands to accomplish that.

$ cd /opt/lampp
$ bin/mysql -u root
mysql> create database wptest;
mysql> GRANT ALL PRIVILEGES ON wptest.* TO wptest@localhost IDENTIFIED BY 'wptest';
mysql> flush privileges;
mysql> exit

Install WordPress

WordPress Tests Library requires WordPress software to create an test instance. In wp-tests-config.php, we defined ABSPATH pointing it the location of WordPress at /opt/lampp/wptest/wordpress/, but you are free to install WordPress in any other location. While defining ABSPATH ensure that path ends with a trailing slash as in /opt/lampp/wptest/wordpress/ otherwise WordPress fails to load.
Download the latest archive of WordPress and extract it with following commands.

$ cd /opt/lampp/
$ mkdir wptest
$ cd wptest
$ unzip wordpress-4.1.zip
$ chown -R xyz.xyz wordpress   // xyz is the user who runs test
$ chmod -R 775 wordpress

User who runs test should have write access to wordpress/wp-content directory and last two commands chown and chmod are used to set the permission to wordpress and its subdirectories.
No further installation is required as WordPress as Tests Library will use wp-tests-config.php to configure the WordPress Instance it creates.

Production WordPress

We can point ABSPATH to any existing WordPress folder and run tests but there is a downside. For some reason, if wp-tests-config.php is missing from tests/phpunit directory, then WordPress Test Library will use wp-config.php of the WordPress folder and drops the WP tables defined by it. Keep this in mind, while pointing ABSPATH to a WordPress folder of production site as you may end up loosing the production database.
Since space required of WordPress software is very small it is much safe to place WordPress in a dedicated test folder such as /opt/lampp/wptest and keep it exclusively for testing. It also allows us to place multiple WordPress version in wptest folder and run tests against multiple versions.

Create Link to Plugin

We had defined Simple Plugin as active plugin in tests/phpunit/bootstrap.php, but Test Library loads only the plugins present in wordpress/wp-content/plugins directory. If we develop the plugin directly from the plugins directory then Test Library finds and activates the plugin without any problem.
Normally, it is much more convenient to develop the plugin from IDE workspace or some other location and in such case, we have to create a symbolic link to plugins’ actual location in wordpress/wp-content/plugins/ directory.
For example, if Simple Plugin is at /home/xyz/work/wp-simple-plugin and WordPress package is at /opt/lampp/wptest/wordpress, then create symbolic with following commands.

$ cd /opt/lampp/wptest/wordpress/wp-content/plugins
$ ln -s /home/xyz/work/wp-simple-plugin wp-simple-plugin

That completes the WordPress PHPUnit Setup. In the next blog, we write a couple of simple unit tests and check whether the setup is working as expected.