12.3. WordPress Plugin Simple Unit Tests

In the previous blog, we setup PHPUnit for WordPress Plugin and verified the setup with a Simple Plugin. The rest of the blog explains various types of unit tests we used to test the Share on Social Plugin.

The real hurdle in testing a plugin is to understand the WordPress architecture and write the appropriate tests for it. For better understanding, we selected various types of methods that are typical to a plugin and explain how to test them. The complete set of tests of Share on Social Plugin is available for download from GitHub.

In this blog, we start with PHPUnit Test Case, Setup, Tear down and then explain a couple of basic types of plugin tests.

Test Case, Setup, Tear Down

WordPress Plugin Simple Unit Tests - test files

In Share on Social Plugin, for each class file there is corresponding test class file in tests/phpunit/tests directory. The test file is a regular PHP class which extends WordPress Tests Librarys' WP_UnitTestCase which itself extends PHPUnit_Framework_TestCase.

The test method names are prefixed with test_ as PHPUnit requires that to distinguish the test methods from others in the test case.

In all test classes, we define a variable with class scope that holds the instance of class under test. In the next snippet, the var $sos will hold an instance of class Sos which is the class under test.

The instance of the class under test is normally created in setup() method.

share-on-social/tests/phpunit/tests/test-sos.php

class Test_Sos extends WP_UnitTestCase {

    var $sos;

    public function setup () {
        parent::setup();
        
        require_once 'admin/class-sos.php';
        $this->sos = new Sos();
    }
    ....
}

In the setup() method, we need to call parent::setup() before doing any other thing. Next, we include the class file of class under test with require_once(). In the above test case, we include admin/class-sos.php which defines the class Sos. Finally, we create an instance of Sos and assign it to the variable $sos. PHPUnit calls setup() before running each test and so, for each test we get a fresh instance of class Sos.

After completion of each test, PHPUnit calls teardown(), where we call parent::teardown() and then any clean up required to run subsequent tests.

share-on-social/tests/phpunit/tests/test-sos.php

class Test_Sos extends WP_UnitTestCase {

    var $sos;

    ....

    public function teardown () {
        parent::teardown();
        unload_textdomain( 'sos-domain' );
        Util::set_activate_plugins_cap( false );
    }
    ....
}

In majority of test cases, in teardown() method, we unload the text domain which we explain in a later blog when we cover the WordPress Internationalization testing. In the above test case, we also remove a user capability which is enabled by some test in this particular test case.

The things we do in setup() and teardown() methods varies for each test case, but they are easy to understand once you go through test class.

 
 

Methods with Return Value

The simplest type of plugin tests are of the methods that return some value. For example, the Sos_Options::sanitize_options() method takes an array as input which hold key, value pairs and the method sanitize the values using WP function wp_filter_nohtml_kses() and add the sanitized value to an output array. The method returns the output array which holds the sanitized values.

share-on-social/admin/class-options.php

    function sanitize_options ( $input ) {
        $output = array();
        foreach ( $input as $key => $val ) {
            if ( isset( $input<a href="https://phpunit.de/manual/current/en/appendixes.assertions.html" target="_blank"> $key ] ) ) {
                // $output[$key] = strip_tags(stripslashes($input[$key]));
                $output[ $key ] = wp_filter_nohtml_kses( $input[ $key ] );
            }
        }
        return apply_filters( 'sanitize_options', $output, $input );
    }

We test this type of methods by asserting whether the returned object contains the expected values.

share-on-social/tests/phpunit/tests/test-options.php

    public function test_sanitize_options_scripts () {
        $input = array(
                'id' => 'test id<h1><?php evil ?>'
        );
        $output = $this->sos_options->sanitize_options( $input );
        $this->assertCount( 1, $output );
        $this->assertSame( 'test id', $output[ 'id' ] );
    }

In the test method, first we construct an input array and add an option value which contains some PHP script tags. Next, we call method under test Sos_Options::sanitize_options() by passing input array as parameter. Then, we test whether the returned output has only one element and whether the method has sanitized the value by removing the PHP tags from the input value. The instance of class Sos_Options is instantiated in test case setup() method and assigned to the variable $this->sos_options.

Always Count

When testing an array, always assert the size of the array with assertCount(). It ensures that test fails whenever you make changes to the code that affects the size of the array, and failure alerts you to write additional assertions to test the changes.

 
 

Methods that Echo Output

One more type of frequently used methods are those which echo output without return value. Plugin uses such methods as callbacks to render pages, menu etc.,

One such example is Sos_Stats::render_stats_page() method from admin/class-stats.php.

share-on-social/admin/class-stats.php

    public function render_stats_page () {
        $heading = __( 'Share Stats', 'sos-domain' );
        echo <<<EOD
        <h3>{$heading}</h3>
        <div>&nbsp;</div>
        <div id="summary_chart"></div>
        <div>&nbsp;</div>
        <div id="stats_chart"></div>        
EOD;
    }

In Share on Social, we test such methods by capturing the echoed output with PHP output buffer functions.

share-on-social/tests/phpunit/tests/test-stats.php

    public function test_render_stats_page () {
        ob_start();
        $this->sos_stats->render_stats_page();
        $result = ob_get_contents();
        ob_end_clean();
        $expected = <<< EOD
        <h3>Share Stats</h3>
        <div>&nbsp;</div>
        <div id="summary_chart"></div>
        <div>&nbsp;</div>
        <div id="stats_chart"></div>    
EOD;
        $this->assertSame( trim( $expected ), trim( $result ) );
    }

PHP output buffer functions are used to capture the output. After turning on the output buffer with PHP function ob_start(), we call the method under test. The echoed output is obtained with ob_get_contents() and after that, buffer is turned off with ob_end_clean(). The result is asserted for desired value.

Multiple Assertions

Unit Test best practice recommends to have single assertion per test which ensures that tests are concise and readable. When test fails, developers can easily pinpoint the error and refactor the code.

But in Share on Social Plugin test cases we have not followed this recommendation. Single assert per test rule results in too many tests and it is bit difficult to browse the test code and comprehend. Instead, we use single test to test single condition by making multiple assertions to test that condition.

If you don’t mind too many test methods and have knack to meaningfully name them, then it is better to stick with the single assertion per test rule.

In the next blog, we explain some of the advanced types of tests that are unique to WordPress Plugin.