Command Line WordPress, Unit Testing, WP API

Using the WordPress JSON REST API in Testing Suites with Travis CI

Problem:

We are writing custom endpoints and routes that extend WP API in a theme or plugin. We want to write tests for this code and run tests in Travis CI. Right now, WP API is not in WordPress core and must be included as a plugin. Therefore, we have a WordPress plugin/theme that depends on another plugin. We want to make sure all our dependencies are installed/included when we bootstrap our testing suite. WP API is not a registered Composer package.

Bad Solution #1:

Complain about WP API not being a registered Composer package. Just joking. The reason behind this is that WP API must remain backwards compatible after being included in WordPress core.

Bad Solution #2:

Include WP API in your theme/plugin as a Git submodule. Not only are Git submodules annoying but this doesn’t even make sense. Git submodules point to a specific commit. We want to just install the latest stable version which isn’t possible using submodules unless we want to manually update our submodule commit hash every time WP API is updated.

Final Solution

We need to manually include WP API in our plugin/theme but we want it up-to-date. We want to pretend WP API is a Composer package. If it’s not already there, create a folder vendor/ in the root of your project. This is the same folder that Composer will store your packages. Add vendor/ to your .gitignore since we don’t need this code in production (assuming you don’t have other packages in vendor that are needed in production).

We can execute bash scripts whenever Composer install/update is called. Let’s write a simple Bash script to clone/update WP API into vendor/. Place this code in bin/install-wp-api.sh:

cloneOutput=$(git clone https://github.com/WP-API/WP-API.git ./vendor/wp-api 2>&1)

if [[ $cloneOutput =~ "destination path './vendor/wp-api' already exists" ]]; then
  cd vendor/wp-api
  git reset --hard &>/dev/null
  git checkout master &>/dev/null
  git reset --hard &>/dev/null
  git pull origin master &>/dev/null
fi

This script will clone WP API, if it doesn’t exist. Otherwise it will checkout the master branch and pull the latest from Github.

Now we need to execute this script. We will use Composer scripts. Here is an example composer.json file:

{
    "name": "tlovett1/package-name",
    "description": "Description of my package.",
    "license": "MIT",
    "authors": [
        {
            "name": "Taylor Lovett",
            "email": "email@email.com"
        }
    ],
    "minimum-stability": "stable",
    "require": {},
    "scripts": {
        "pre-install-cmd": [
            "./bin/install-wp-api.sh"
        ],
        "pre-update-cmd": [
            "./bin/install-wp-api.sh"
        ]
    }
}

composer.json defines your package as a Composer package, registers it’s dependancies, and more. As I mentioned before, we can call scripts as well. We use the pre-install-cmd hook to execute our script every time composer install is called. We use the pre-update-cmd hook to execute our script every time composer update is called. On both of these hooks we are executing our script at ./bin/install-wp-api.sh.

Now we simply add the following to our .travis.yml file:

before_script:
   - composer install

In your test suite bootstrap file, you can insert the following PHP to manually use WP API assuming you are using the WordPress standard unit testing suite:

function _manually_load_plugin() {
	require( dirname( __FILE__ ) . '/../vendor/wp-api/plugin.php' );
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
Command Line WordPress, Node.js, WordPress for Enterprise

Run WordPress Cron on Real Unix Cron with Node.js

WordPress cron is a confusing beast. Most people don’t understand it or intentionally use it. It is not the same technology as Unix cron. Scheduled post functionality actually depends on WordPress cron. By default on every page load, WordPress checks to see if any cron events are due to fire. If an event is due, it sends a request to wp-cron.php asynchronously to execute the event(s).

So what?

This system works great for small websites running simple theme and plugin setups. Often when building WordPress applications for enterprise, enough resource intensive events get setup on cron that HTTP requests timeout before completion. We can circumvent this problem by executing WordPress cron using actual cron.

wp-cron-node is a simple node command that runs WordPress cron events via PHP CLI and Unix cron. It will execute WordPress cron events that are due for execution. The command requires WP-CLI.

Let’s run our scheduled events using actual Unix cron and Node:

  1. Make sure npm and WP-CLI are installed.
  2. Run the following command:
    npm install -g wp-cron-node
  3. Disable WordPress cron. This will prevent HTTP requests from triggering cron events. Put this code in wp-config.php:
    define( 'DISABLE_WP_CRON', true );
  4. Finally, setup your crontab file to your liking. Edit your crontab with the following command:
    crontab -e

    Here is an example crontab entry that will check for scheduled events every 10 minutes.

    */10 * * * * wp-cron-node /path/to/wp

    Note: Running this every 10 minutes means cron events could fire up to 10 minutes late. Ten is a conservative number for performance reasons. Change this to whatever makes you comfortable.

Backbone, WordPress Code Techniques

Syncing Backbone Models and Collections to admin-ajax.php

Backbone is framed around the assumption that your models and collections are backed by RESTful API’s. If this isn’t the case, life becomes difficult. WordPress provides an AJAX API for backend AJAX requests. These requests are sent through admin-ajax.php which is not RESTful.

I’ve written some code to force Backbone models and collections to play nicely with admin-ajax.php. First I’ll show code, then provide explanation:

/**
 * A mixin for collections/models
 */
var adminAjaxSyncableMixin = {
	url: ajaxurl,

	sync: function( method, object, options ) {
		if ( typeof options.data === 'undefined' ) {
			options.data = {};
		}

		options.data.nonce = localizedSettings.nonce;
		options.data.action_type = method;
		options.data.action = 'myaction';

		var json = this.toJSON();
		var formattedJSON = {};

		if ( json instanceof Array ) {
			formattedJSON.models = json;
		} else {
			formattedJSON.model = json;
		}

		_.extend( options.data, formattedJSON );

		options.emulateJSON = true;

		return Backbone.sync.call( this, 'create', object, options );
	}
};

/**
 * A model for all your syncable models to extend
 */
var BaseModel = Backbone.Model.extend( _.defaults( {
	parse: function( response ) {
		// Implement me depending on your response from admin-ajax.php!

		return response
	}

}, adminAjaxSyncableMixin ));

/**
 * A collection for all your syncable collections to extend
 */
var BaseCollection = Backbone.Collection.extend( _.defaults( {
	parse: function( response ) {
		// Implement me depending on your response from admin-ajax.php!

		return response
	}

}, adminAjaxSyncableMixin ));

The bulk of the action happens in our mixin:

var adminAjaxSyncableMixin = {
	url: ajaxurl,

	sync: function( method, object, options ) {}
};

We will extend the defining objects passed to our base model and collection. Therefore BaseCollection and BaseModel will have this objects properties (unless they are overwritten — more on that later).

The url property defines the location to which syncing will occur. In this case we are using ajaxurl which is a variable localized from WordPress by default containing a full URL to admin-ajax.php.

The sync property defines a function that will be called in front of Backbone.sync. Backbone.sync is called whenever we call Backbone.Model.save, Backbone.Model.destroy, Backbone.Model.fetch, Backbone.Collection.save, and Backbone.Collection.fetch. By providing a sync property to our base model and collection, we are forcing our sync function to be called instead of Backbone.sync.

adminAjaxSyncableMixin.sync has a few parameters:

sync: function( method, object, options ) {

}

By default, method is set by the function called. For example, calling Backbone.Model.save or Backbone.Collection.save will call Backbone.sync where method is create or patch (possibly update). method ultimately defines the type of HTTP method used (GET, POST, DELETE, PATCH, PUT, or OPTIONS). object is the model or collection object being synced. options lets us send associative arguments to our sync destination among other things.

Let’s look at the body of this function.

if ( typeof options.data === 'undefined' ) {
	options.data = {};
}

Setting the data property in options lets us manually overwrite the representational data sent to the server. We are going to use this so we make sure it’s defined, if it isn’t.

options.data.nonce = localizedSettings.nonce;
options.data.action_type = method;
options.data.action = 'myaction';

Now we are just setting up information to pass to admin-ajax.php. By default we pass a nonce that has been localized to our script. options.data.action should contain the action slug registered within WordPress using the wp_ajax_ hook. We will force our request to be an HTTP POST so we send our method along inside action_type for later use.

var json = this.toJSON();
var formattedJSON = {};

if ( json instanceof Array ) {
	formattedJSON.models = json;
} else {
	formattedJSON.model = json;
}
_.extend( options.data, formattedJSON );

This code sets up our model or collection data to be passed to the server. If the toJSON() representation of the current object is an Array, we know we have a collection. We extend the options.data object with the formattedJSON object we create.

options.emulateJSON = true;

This sends our data as application/x-www-form-urlencoded (classic form style) instead of application/json preventing us from having to decode JSON in our endpoint.

return Backbone.sync.call( this, 'create', object, options );

Finally, we call Backbone.sync in the current object context. We pass create as the method (forcing a POST request). object is simply passed along. We pass options having extended it with our own data. Essentially, our sync function is an intermediary between Backbone save/fetch and Backbone.sync.

var BaseModel = Backbone.Model.extend( _.defaults( {
	idAttribute: 'ID',
	parse: function( response ) {
		// Implement me depending on your response from admin-ajax.php!

		return response
	}
}, adminAjaxSyncableMixin ));

var BaseCollection = Backbone.Collection.extend( _.defaults( {
	parse: function( response ) {
		// Implement me depending on your response from admin-ajax.php!

		return response
	}
}, adminAjaxSyncableMixin ));

We define BaseModel by extending Backbone.Model and mixing in adminAjaxSyncableMixin. _.defaults returns an object filling in undefined properties of the first parameter object with corresponding property in the second parameter object. We define BaseCollection the same way extending Backbone.Collection and mixing in adminAjaxSyncableMixin.

Backbone.Model.parse and Backbone.Collection.parse intercept sync responses before they are processed into models and model data. Depending on how you write your admin-ajax.php endpoints, you may need to write some parsing code.

Finally, we can define, instantiate, and utilize new models and collections based on BaseModel and BaseCollection:

var myModel = BaseModel.extend({});
var myCollection = BaseCollection.extend({});

var modelInstance = new myModel( { ID: 1 } );
modelInstance.fetch();
modelInstance.set( 'key', 'value' );
modelInstance.save();

var collectionInstance = new myCollection();
collectionInstance.fetch();
collectionInstance.at( 0 ).set( 'key', 'value' );
collectionInstance.save();
Presentations, WordPress Core

JSON REST API for WordPress at the DC API User Group

Today I am presenting on the JSON REST API for WordPress at the DC API User Group. This is a shorter talk geared at both developers and API users with or without WordPress experience.

With core integration coming in the near future, it’s important for developers of ALL backgrounds to understand it will be available on ~23% of the websites on the internet. Here are the slides for my talk:

WordPress Core, WordPress Themes, WP API

A WordPress Starter Theme Based on JSON REST API’s Backbone Client

The past few months I’ve had the opportunity to work on the new JSON REST API for WordPres. My biggest contribution as a WP API team member has been the Backbone client.

The JSON REST API’s Backbone client let’s you interact with a WordPress installation using Backbone.js collections and models. The client is an extremely useful tool in creating reactive web applications (which seems to be where the web is heading).

As a proof of concept, I created a WordPress starter theme based on Automattic’s _s named _s_backbone. Loops (or post streams) in _s_backbone are driven by Backbone.js collections. This means that posts are grabbed on the fly without a page reload. Pagination is accomplished through a “more” button which, again, does not require a page reload. This is commonly referred to as “infinite scroll”.

Please download _s_backbone from Github. Any feedback is appreciated.

Edit: Check out the WP Tavern article on _s_backbone.

Node Module for Detecting Modified Core WordPress Files

Modifying core WordPress files is a quick way to introduce security vulnerabilities, break your site, and break future updates. From the perspective of a developer, nothing is worse than searching through a theme and plugins for a problem only to discover a previous developer modified a core WordPress file an introduced a bug.

I wrote a simple Node module that lets you easily check WordPress installations for modified or removed core files. Install the detect-wp-core-modifications npm package with the following shell command:

npm install -g detect-wp-core-modifications

You can run the command without any arguments from within the root of a WordPress installation with the following shell
command:

detect-wp-core-modifications

You can also specify a relative or absolute path to a WordPress installation:

detect-wp-core-modifications ../wordpress

Granularly Manage Per-Post Access with Editorial Access Manager Plugin

Given the unmet need to allow users only access to edit specific pieces of content, I created a simple plugin called Editorial Access Manager.

By default in WordPress, we can create users and assign them to roles. Roles are automatically assigned certain capabilities. However, sometimes default roles are not enough, and we have one-off situations. Editorial Access Manager lets you set which users or roles have access to specific posts (as well as pages and custom post types).

Only the Administrator and Editor role have access to manage pages by default in WordPress. Making a user an Editor gives them a lot of power. I ran into the situation where we wanted a Contributor (less power than an Editor) to be able to edit only a few pages hence the need for this plugin.

My plan is to eventually integrate this plugin with EditFlow allowing people to assign User Groups to have access to specific pieces of content.

Any development help on this project would be much appreciated! Fork the Github repo.

Unit Testing, WordPress Code Techniques, WordPress Core

WP_UnitTestCase: The Hidden API

Background:

Unit testing is the automated testing of units of source code against assumptions. The goal of unit testing is to write test cases with assumptions that test if a unit of code is truly working as intended. If an assumption fails, a potential issue is exposed, and code needs to be revised.

By definition unit tests do not have dependencies on outside systems; in other words only your code (a single unit of code) is being tested. Integration testing works similarly to unit tests but assumptions are tested against systems of code, moving parts, or an entire application. The phrases unit testing and integration testing are often misused to reference one another especially in the context of WordPress. This article will try to stay consistent with WordPress terminology whether correct or not. Personally, in writing tests for my WordPress plugins/themes I write integration tests which I feel are more useful for a number of reasons that are outside of the realm of this article.

PHPUnit is the defacto library used to run unit and integration tests in PHP and thus server-side WordPress. This article assumes you are familiar with basic concepts of unit testing with PHPUnit.

What is WP_UnitTestCase?

WP_UnitTestCase is a PHP class included in the WordPress core development repository. Using SVN, you can check out that repo like so:

svn co http://develop.svn.wordpress.org/trunk/ wordpress-develop

The WordPress core development repository contains the core WordPress unit tests. Cool, huh? You can setup these tests, run them, and even write your own! Let’s poke around a few WordPress core test files. Open up wordpress-develop/tests/phpunit/tests/actions.php.

Normally, PHPUnit tests are structured like so:

class MyTestSuite extends PHPUnit_Framework_TestCase {
  public function setUp() {
    // This code will run before each test!
  }

  public function tearDown() {
    // This code will run after each test
  }

  public function testBasic() {
    $this->assertTrue( true );
  }
}

However, when writing integration tests for WordPress based applications, there exists a cool API we can leverage: WP_UnitTestCase. Remember how I asked you to open the actions.php file? You will see the test class in there extends WP_UnitTestCase. WP_UnitTestCase provides shortcuts for doing a number of tasks when creating your test cases. The first thing you will need to do to use this API is to require the file in your PHPUnit bootstrap file:

require_once( $tests_dir . '/includes/bootstrap.php' );

Of course you will need to setup $tests_dir appropriately. Within the WordPress core development repository, this bootstrap file lives at /tests/phpunit/includes/bootstrap.php. Including this bootstrap file will “bootstrap setup” WordPress, setup some things for unit testing, and include a number of useful things one of which is WP_UnitTestCase. There are other ways you could include WP_UnitTestCase, but this is by far the easiest.

Let’s setup our test class to use WP_UnitTestCase:

class MyTestSuite extends WP_UnitTestCase {
  public function setUp() {
    parent::setUp();
    // This code will run before each test!
  }

  public function tearDown() {
    parent::tearDown();
    // This code will run after each test
  }

  public function testBasic() {
    $this->assertTrue( true );
  }
}

Notice our class is extending WP_UnitTestCase instead of PHPUnit_Framework_TestCase. You can probably guess that WP_UnitTestCase extends PHPUnit_Framework_TestCase so we can use everything in PHPUnit_Framework_TestCase by extending WP_UnitTestCase plus a bunch more. Also notice within setUp() and tearDown() we are calling the same methods in the parent class; this is extremely important to ensure WP_UnitTestCase is working correctly. WP_UnitTestCase::setUp() and  WP_UnitTestCase::tearDown() do a bunch of magic such as cache clean up and global resets.

WP_UnitTestCase has an instance variable named $factory. Before each of your tests is run $factory is set to a newly instantiated object of type WP_UnitTest_Factory. The meat of WP_UnitTestCase is in this $factory variable. Let’s look some cools way that we can use WP_UnitTestCase.

Creating Blogs within a Network

public function testBasic() {
  $blog_id = $this->factory->blog->create();
  $blog_id_array = $this->factory->blog->create_many( 4 );
}

This is extremely useful for writing test cases that make cross-site assumptions. As you can tell $this->factory->blog->create() creates a blog and returns the new blog ID. $this->factory->blog->create_many( 4 ), creates 4 new blogs and returns an array of the ID’s in order.

If multisite is not setup, this factory will not exist! You can ensure WordPress is bootstrapped as multisite by setting the following constant up in your phpunit.xml file:

<const name="WP_TESTS_MULTISITE" value="1" />

Creating Test Posts

public function testBasic() {
  $post_id = $this->factory->post->create();
  $post_id_array = $this->factory->post->create_many( 4 );
}

Many times when I am writing tests I need to bulk create posts. WP_UnitTestCase let’s you accomplish this very easily. You can pass an array of post args (same as wp_insert_post) like so: create( $args ) and create_many( $num, $args ). If no arguments are passed, generic post_title/post_content info is generated. Post args passed to create_many will be applied to every post created.

Creating Test Comments

public function testBasic() {
  $comment_id = $this->factory->comment->create();
  $comment_id_array = $this->factory->comment->create_many( 4 );
}

Again, we can leverage WP_UnitTestCase to bulk create comments. You can see the ways to use the class is very consistent :). Comment args (like wp_insert_comment) can be passed to create() and create_many() in the exact same way as the post factory.

Creating Test Users

public function testBasic() {
  $user_id = $this->factory->user->create( array( 'user_login' => 'test', 'role' => 'administrator' ) );
  $user_id_array = $this->factory->user->create_many( 4 );
}

WP_UnitTestCase allows us to bulk create users as well. User args (same as wp_insert_user) can be passed to create() and create_many() in the same fashion as the post factory.

Creating Test Terms

public function testBasic() {
  $term_id = $this->factory->term->create( array( 'name' => 'Term Name', 'taxonomy' => 'category', 'slug' => 'term-slug' ) );
  $term_id_array = $this->factory->term->create_many( 4, array( 'taxonomy' => 'category' );
}

Creating bulk terms is slightly different from other factories. Create() and create_many() do take arguments the same was as wp_insert_term(). However, we provide ‘taxonomy’ and ‘name’ as a part of the array rather than as separate parameters.

Conclusion

WP_UnitTestCase contains additional factories for categories, tags, networks, and attachments. They work in a similar fashion as the factories above, so I will let you explore these API’s yourself :). WordPress contains a bunch of other useful API’s for building your test suites such as a system for mocking actions and a test case class for AJAX hooks. I plan on outlining those tools and a few others in future blog posts.

Database Theory, WordPress Code Techniques

A Performant Way to Feature Posts

In a past blog post I explained why featuring posts using a taxonomy term is much more performant than using a meta query. The comment I get from people is “that’s awesome, but using tags to feature a post is not a good user experience”. I agree, attaching a “featured” tag to featured posts, while performant is not a good experience for users because it leaves room for error on the admin side and shows the “featured” tag to users on the front end (if you are listing your tags).

Thankfully, there is a much better way to do this. We can create a small meta box with a “Featured Post” checkbox. This checkbox will add/remove a term in a hidden taxonomy from the post. Here is what the meta box will look like in WordPress 3.9:

Featured post meta box

I will take you through the code necessary to set this up. First we need to register a private taxonomy for internal use:

function tl_register_taxonomy() {
  $args = array(
    'hierarchical' => false,
    'show_ui' => false,
    'show_admin_column' => false,
    'query_var' => false,
    'rewrite' => false,
  );

  register_taxonomy( 'tl_post_options', array( 'post' ), $args );
}
add_action( 'init', 'tl_register_taxonomy' );

Now let’s write the code that actually associates the taxonomy term with featured posts. This will hook onto the “save_post” action.

function tl_save_post( $post_id ) {
  if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) || 'revision' == get_post_type( $post_id ) )
   return;

  if ( ! empty( $_POST['additional_options'] ) && wp_verify_nonce( $_POST['additional_options'], 'additional_options_action' ) ) {
    if ( ! empty( $_POST['tl_featured'] ) ) {
      $featured = term_exists( 'tl_featured', 'tl_post_options' );
      if ( empty( $featured ) ) {
        $featured = wp_insert_term( 'tl_featured', 'tl_post_options' );
      }
      wp_set_post_terms( $post_id, array( (int) $featured['term_id'] ), 'tl_post_options' );
    } else {
      wp_set_post_terms( $post_id, array(), 'tl_post_options' );
    }
  }
}
add_action( 'save_post', 'tl_save_post' );

Next, we need to output a meta box with a checkbox. If this checkbox is checked, the post is marked as featured and the appropriate information is sent to the “save_post” hook on POST.

function tl_meta_box_additional_options( $post ) {
  wp_nonce_field( 'additional_options_action', 'additional_options' );
  $featured = has_term( 'tl_featured', 'tl_post_options', $post );
  echo 'Featured: <input type="checkbox" name="tl_featured" value="1" ' . ( ( $featured ) ? 'checked="checked"' : '' ) . '>';
}

Don’t forget we need to actually register our new meta box:

function tl_add_meta_boxes() {
  add_meta_box( 'tl_additional_options', 'Additional Options', 'tl_meta_box_additional_options', 'post', 'side' );
}
add_action( 'add_meta_boxes', 'tl_add_meta_boxes' );

Querying for posts on the front end is super easy! Here is an example query:

$query = new WP_Query( array(
  'tl_post_options' => 'tl_featured'
  'post_status' => 'publish',
  'post_type' => 'post',
) );