Skip to content

Using MockPress to Test Your Plugin or Theme

johnbintz edited this page Sep 13, 2010 · 10 revisions

Unit testing a WordPress plugin or theme is best done when you can separate the majority of the plugin or theme into separate logical classes.

(Note: all examples below use PHPUnit for any example code)

Basics

Installation

MockPress is now available as a PEAR package:

pear upgrade -f http://www.coswellproductions.com/mockpress/pear/latest.tgz

Once installed, include it in your test cases:

<?php
require_once('PHPUnit/Framework.php');
require_once('MockPress/mockpress.php');

Directory Structure (PHPUnit-specific)

For simplicity’s sake, place all of your PHPUnit test files within a folder inside of your plugin directory called test, and have their filename end in Test.php:

myplugin/
  classes/
    MyObject.php
  test/
    MyObjectTest.php

You can then execute all of your plugin’s PHPUnit tests with one command:

cd /var/www/mysite/wp-content/myplugin/
phpunit test

Test Setup

In order to maintain the state of MockPress, a global variable, $wp_test_expectations, is populated and accessed by the MockPress functions. To ensure a clean WordPress environment on each test case run, in the setUp() method of the test case, call the _reset_wp() function:

class MyObjectTest extends PHPUnit_Framework_TestCase {
  function setUp() {
    _reset_wp();
  }
}

Constructors

To make testing easier, objects should have no or very simple consturctors. If you find yourself writing a lot of setup code to handle class initialization, it’s probably better to move the initialization into a separate method that is called during the ‘init’ action hook, and then mock up any of the needed initialization parts in your test code:

Change:

class MyObject {
  var $variable_to_be_initialized;
  function __construct() {
    $this->variable_to_be_initialized = do_some_complex_thing();
  }
}

$o = new MyObject();

To:

class MyObject {
  var $variable_to_be_initialized;
  function __construct() {}

  function init() {
    $this->variable_to_be_initialized = do_some_complex_thing();
  }
}

$o = new MyObject();
add_action('init', array(&$o, 'init'));

Testing $wpdb

$wpdb is the global variable that contains the connection to the WordPress database. If your plugin or theme needs direct access to the database, you’ll need to use a Mock Object to simulate the $wpdb object.

class MyTableAccessorTest extends PHPUnit_Framework_TestCase {
  function setUp() {
    global $wpdb;
    unset($wpdb); // unsetting $wpdb ensures that your tests always create new mock objects for their use
    _reset_wp();
  }

  function testReadRows() {
    global $wpdb;

    $wpdb = $this->getMock('wpdb', array('get_var'));
    $wpdb->expects($this->once())->method('get_var')->will($this->returnValue(array('1'));

    $mta = new MyTableAccessor();
    $this->assertEquals(1, $mta->get_status());
  }
}

class MyTableAccessor {
  function get_status() {
    global $wpdb;

    return $wpdb->get_var('SELECT status from my_table');
  }
}

WordPress Class Construction Strategies

  • Keep methods small. Have them perform only one action (create, read, update, delete) as best as you can.
  • In admin code, save testing the construction of user interfaces for acceptance testing, using Selenium or similar test harnesses to make sure the HTML/JS/CSS you’re producing is correct.
  • When handling user input, place as much of the update code into separate classes as you can. The code that handles user input should, ideally, be only passing that user input into methods on classes that do the actual changes to the WordPress database.

I really want to test XML/XHTML output for correctness. How do I use the XPath tests?

Use a structure like this:

ob_start();
$plugin->render_something();
$result = ob_get_clean();

$this->assertTrue(($xml = _to_xml($result, true)) !== false);

foreach (array(
  '//h2[@id="this-should-exist"]' => true,
  '//h3[@id="this-should-not-exist"]' => false,
  '//h4[@id="this-contains-something"]' => "what it contains"
) as $xpath => $value) {
  $this->assertTrue(_xpath_test($xpath, $value), $xpath);
}
Clone this wiki locally
You can’t perform that action at this time.