PHP Developer

My notes and thoughts about Linux, WordPress, PHP and many more.

WP_Mock PHPUnit Testing Framework to WordPress Plugin: Complete Guide

Introduction

WP_Mock: ( https://github.com/10up/wp_mock ) is an API mocking framework that can be used for unit testing WordPress Plugins. This tutorial is a complete beginner guide to add and integrate WP_Mock mocking framework to your WordPress Plugin. WP_Mock uses Mockery PHP mock object framework for unit testing WordPress plugins. This is how to properly implement unit test inside WordPress plugins and can be considered as one of the best practices. If you are already familiar with mocking and unit testing, you can skip the introduction below and proceed directly to requirements and steps section.

Below are some introduction for beginners:

  • It allows developer to “mock” test data, objects, properties, classes and methods to be used during the testing. In short, you don’t need to rely on the WordPress core environment to be loaded just test your plugin. This minimize test time, complexity and improve efficiency of your tests. Other synonyms of “mock” as used with testing includes “fake”, “staged”, “planned” or “simulate”.
  • Since there is no loading of WordPress environment during this testing. It does not actually use real database or actual $wpdb object methods to read and transact testing data from database tables. Again this is mocked or simulated when using WP_Mock and Mockery.
  • All WordPress core functions are mocked as well. A common example is if you are getting post from a database using get_post() or retrieving users using get_users(). You are not actually calling these functions since WordPress is not loaded when using WP_Mock. You define and set this in advance before actually calling one of your plugin method that you need to test.
  • Since dependencies are mocked except with the method you are testing, you can be confident that the test pass or fail entirely depends on the “unit” being tested and not with the other dependencies.

This tutorial works best with a Linux environment such as Ubuntu. Also it assumes you will be using GitHub as your code repository manager to store your plugin. Although concepts can be applied to other UNIX based OS such as Mac or other git repository manager such as

Requirements

  • You should have git already installed. If this is the not case, you can simply install it with:
    sudo apt-get update
    sudo apt-get install git

    You will use git to actually install your plugin WP_Mock library and its dependencies from GitHub.

  • You should have composer installed. In Ubuntu, you can install this easily by:
    sudo apt-get update
    sudo apt-get install composer

    Composer is used to install third party dependencies that is needed by `WP_Mock` such as Mockery, phpunit library, etc.

  • Latest phpunit library now requires at least PHP 7.0, so make sure you are using this version in your test environment. You can get the PHP version in the command line using: php --version. You should get at least PHP 7.0 on it:
    ~$ php --version
    PHP 7.0.27-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Jan  5 2018 14:12:46) ( NTS )
    Copyright (c) 1997-2017 The PHP Group
    Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
        with Xdebug v2.6.0beta1, Copyright (c) 2002-2017, by Derick Rethans
        with Zend OPcache v7.0.27-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2017, by Zend Technologies
  • You should be connected to the Internet when you integrate WP_Mock with your plugin. This is required to fetch all required third party libraries.

Plugin Structure

Assuming you have the following plugin structure:

You have a plugin folder called “wp-mock-test-demo“. Under this main plugin folder, you will find the main plugin file wp-mock-test-demo.php and the include folder where the plugin classes are stored.

The above structure is common to many plugin nowadays. Again that’s an example only and its simplified for the sake of this tutorial.

Steps

Now you have all the requirements set, below are the steps to integrate WP_Mock to the above example plugin.

  1. Create a composer.json file. Below are the contents of the composer.json file for the above test plugin:
    {
        "name": "codex-m/wp-mock-test-demo",
        "description": "Basic plugin for demonstrating WP_Mock unit testing integration.",
        "authors": [
            {
                "name": "Emerson Maningo",
                "email": "[email protected]"
            }
        ],
        "require-dev": {
            "10up/wp_mock": "dev-master",
            "phpunit/phpunit": "^6.5"
        },
      "autoload": {
        "classmap": [
          "includes/"
        ]
      }
    }

    This composer.json should be located in your main plugin root directory. In the above plugin example, the resulting path of composer.json is /wp-content/plugins/wp-mock-test-demo/composer.json

    Explanation to the above composer.json settings:

    • name, description, authors should be self-explanatory. Just edit them and put your own values there.
    • 10up/wp_mock should point to dev-master, so you will always get the very latest WP_Mock releases
    • phpunit/phpunit should point to the very latest version. Currently (as of January 2018) its 6.5 and above. You should continually check the phpunit installation page and scroll down to “Composer” section to get the recommended version to use, e.g. screenshot:

      You can also check the phpunit release page in GitHub. Example, latest version (as of the time this tutorial is written is 6.5.5):
    • Under autoload -> classmap is where you will put the path of your plugin classes that you need to test. Example classmap has a value of includes/ because this is where the sample plugin classes are located. Composer will autoload these classes automatically for testing with phpunit.
  2. Create phpunit.xml. Please add this also under your main plugin root directory example at this path: /wp-content/plugins/wp-mock-test-demo/. The following is the code of phpunit.xml for the demo plugin under testing:
    <phpunit bootstrap="unit-tests/bootstrap.php"
             colors="true">
        <testsuites>
            <testsuite>            
    	   <directory prefix="test-" suffix=".php">unit-tests/tests</directory>	
            </testsuite>
        </testsuites>
        <groups>
            <exclude>
                <group>skipped</group>
            </exclude>
        </groups>
    </phpunit>

    phpunit.xml is the phpunit configuration file. Based on the above configuration, it tells the following information:

    • The bootstrap path is in unit-tests/bootstrap.php
    • Your test files should be found in unit-tests/tests

    You will be creating these files and directories later on. Take note that since this is a configuration file, it can be customized to fit your own test environment. (e.g. change the path names and test folders,etc.)

  3. According to your phpunit.xml, you should be creating a bootstrap.php file under a directory called unit-tests. Create this directory first and then add the bootstrap file inside it. This should be the code of your bootstrap file:
    <?php
    /**
     * Autoload the composer items.
     */
    require_once 'vendor/autoload.php';

    The purpose of this script is to auto load the composer items that will be using for your testing. Your bootstrap.php should be found in: /wp-content/plugins/wp-mock-test-demo/unit-tests/. The script vendor/autoload.php will be automatically created when you finally install the test dependencies with composer later on.

  4. Also according to your phpunit.xml, you should be creating a tests directory under the unit-tests directory. So go ahead and create this tests directory. This is where you will put your unit test files later on.
  5. Now you are all set, next step to run composer to install all required dependencies. In the command line, run this command at your plugin root directory:
    composer install

    The installation can take a while depending on your Internet connection. Wait until all is completed. This installation processes creates new directory called “vendor” and there are several sub-directories and files under it.

Now you are all set. This is how the demo plugin structure would look like after completing the above integration and installation of third party libraries for using with your tests.

Writing your first unit test

Once the above installation of the third party dependencies are done with composer. You are now ready to write your first unit test with WP_Mock! Let’s use the above example plugin in writing test. Supposing the plugin class class-wp-mock-demo-plugin.php contains the following code:

<?php 
/**
 * WP Mock Demo Plugin Class
 * @author emerson
 *
 */
class WP_Mock_Demo_Plugin {
    
    /**
     * Init hooks
     */
    public function init_hooks() {
        add_filter( 'document_title_parts',  array( $this, 'append_login_status_to_title' ) , 10, 1 );        
    }
    
    /**
     * Append login status to title
     * @param string $title
     */
    public function append_login_status_to_title( array $title ) {
        if ( is_user_logged_in() ) {
            $title[] = 'USER LOGGED-IN';
         }
        return $title;
    }    
    
}

Based on the class code, there are two methods. The first method init_hooks is meant to initialize plugin hooks. The second method is the callback to the WordPress filter document_title_parts. The objective is to append login status to the title of the WordPress post or pages. Supposing you will test that these two methods works as intended. You will need to write two unit tests:

  • Test init_hooks method to ensure that it initialize hooks
  • Test append_login_status_to_title to ensure that it appends the text USER LOGGED-IN to the title tag when a user is logged-in

Finally to write your own unit test:

  1. Create a PHP file called test-wp-mock-demo-plugin.php and save it under unit-tests/tests
  2. Inside this test file, add this default code:
    <?php
    /**
     * Tests WP_Mock_Demo_Plugin
     * 
     */
    class Test_WP_Mock_Demo_Plugin extends PHPUnit\Framework\TestCase {
    	/**
    	 * Setup WP_Mock for each test
    	 */
    	public function setUp() {
    		\WP_Mock::setUp();
    	}	
    	/**
    	 * Clean up after the test is run
    	 */
    	public function tearDown() {
    	    $this->addToAssertionCount(
    	        \Mockery::getContainer()->mockery_getExpectationCount()
    	        );
    		\WP_Mock::tearDown();
    	}	
    }

    This is your default WP_Mock test template file. Every time your add a new test file, start with this template. It consists of adding a basic setup and teardown of WP_Mock test.

  3. Now its time to add your actual tests. The following are the complete code of the tests:
    <?php
    /**
     * Tests WP_Mock_Demo_Plugin
     * 
     */
    class Test_WP_Mock_Demo_Plugin extends PHPUnit\Framework\TestCase {
    	/**
    	 * Setup WP_Mock for each test
    	 */
    	public function setUp() {
    		\WP_Mock::setUp();
    	}	
    	/**
    	 * Clean up after the test is run
    	 */
    	public function tearDown() {
    	    $this->addToAssertionCount(
    	        \Mockery::getContainer()->mockery_getExpectationCount()
    	        );
    		\WP_Mock::tearDown();
    	}
    	/**
    	 * Instantiate an instance of the class to be tested
    	 * @return WP_Mock_Demo_Plugin
    	 */
    	private function get_subject() {
    	    $test_subject = new WP_Mock_Demo_Plugin();
    	    return $test_subject;
    	}
    	/**
    	 * @test
    	 * Test that hooks are initialized
    	 */
    	public function it_adds_init_hoooks() {
    	    //Get an instance of the subject to be tested
    	    $test_subject = $this->get_subject();
    	    
    	    /**
    	     * Ensure the filter added
    	     * Documentation https://github.com/10up/wp_mock#mocking-actions-and-filters
    	     */
    	    \WP_Mock::expectFilterAdded( 'document_title_parts',  array( $test_subject, 'append_login_status_to_title' ) , 10, 1 );
    	    
    	    //Now test the init hook() method of this class to check if this filter is really added
    	    $test_subject->init_hooks();
    	}
    	/**
    	 * @test
    	 * Test that it appends USER LOGGED-IN to title when user is logged-in
    	 */
    	public function it_appends_user_loggedin_to_title() {
    	    //Get an instance of the subject to be tested
    	    $test_subject = $this->get_subject();
    	    
    	    /**
    	     * Mock 'is_user_logged_in' WordPress core function
    	     * Documentation: https://github.com/10up/wp_mock#mocking-wordpress-core-functions
    	     */
    	    \WP_Mock::userFunction( 'is_user_logged_in', array(
    	        'times' => 1,
    	        'return' => true
    	    ) );
    	    
    	    //Mock original title
    	    $original_title = array();
    	    $original_title[] = 'Original title';
    	    
    	    //Set expected result
    	    $expected_result = $original_title;
    	    $expected_result[] = "USER LOGGED-IN";
    	    
    	    //Now test the append_login_status_to_title() to make sure that filters title and appends USER LOGGED-IN text	    
    	    $filtered_title = $test_subject->append_login_status_to_title( $original_title );
    	    
    	    //Now let's assert that the filtered title is one we expected.
    	    $this->assertSame( $expected_result, $filtered_title );	    
    	    
    	}
    	
    }

Please read the code comments in the test for details. Explaining this is beyond the scope of this tutorial. For more information, please refer to these following documentations:

WP_Mock Basic Usage
PHPUnit Assertions

Running your unit test

To run the test, execute this at the terminal:

./vendor/bin/phpunit --configuration phpunit.xml

The test results will be shown like the one below:

$ ./vendor/bin/phpunit --configuration phpunit.xml
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 31 ms, Memory: 6.00MB

OK (2 tests, 3 assertions)

Download the sample demo plugin

You can get the sample demo plugin used in this tutorial in GitHub: https://github.com/codex-m/wp-mock-test-demo

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

This site uses Akismet to reduce spam. Learn how your comment data is processed.