Tag Archives: Unit Tests

PHPUnit and the WordPress file system API

If you unit test your WordPress projects with PHPUnit, sooner or later you’ll want to test some code that interacts with the filesystem. If you are interacting with the filesystem properly, you’ll be using the $wp_filesystem object.

A few months ago I found myself needing to test some code that created a file using the WordPress filesystem API. I searched for a good solution, and found the information about mocking the filesystem on the PHPUnit website. But after tinkering with vfsStream, I decided it would be easier to build a simple filesystem mocker myself than to integrate vfsStream with the WP Filesystem API. So I did.

The result is the WP Filesystem Mock.

[It] Provides a class that can be used as a mock filesystem, and also a shim for the WordPress filesystem API that uses it. This is useful in unit tests that include simple filesystem operations.

Enjoy!

Creating Your Own PHPUnit @requires Annotations

PHPUnit offers a feature that lets you skip a test when certain requirements aren’t met. This can be done in two ways:

  1. You can manually check if the requirements are met, and then skip the test with $this->markTestSkipped() if they are not.
  2. In some cases, you can use the @requires annotation, and the test will be skipped automatically when the requirements aren’t met.

Using the @requires annotation is nicer, but PHPUnit only has so many options built in. Sometimes you have custom requirements that can’t really be checked reliably with any of the built-in options. An example is when you need some tests you’ve written for a WordPress plugin to run only when WordPress’s multisite feature is enabled on the test site. In my tests, I find myself needing this a lot. So I’ve been writing this over and over:

if ( ! is_multisite() ) {
     $this->markTestSkipped( 'Multisite must be enabled.' );
}

But just yesterday I realized that this was silly, and that I could easily add my own custom @requires annotation. So I did. Here is the code:

	protected function checkRequirements() {

		parent::checkRequirements();

		$annotations = $this->getAnnotations();

		foreach ( array( 'class', 'method' ) as $depth ) {

			if ( empty( $annotations[ $depth ]['requires'] ) ) {
				continue;
			}

			$requires = array_flip( $annotations[ $depth ]['requires'] );

			if ( isset( $requires['WordPress multisite'] ) && ! is_multisite() ) {
				$this->markTestSkipped( 'Multisite must be enabled.' );
			} elseif ( isset( $requires['WordPress !multisite'] ) && is_multisite() ) {
				$this->markTestSkipped( 'Multisite must not be enabled.' );
			}
		}
	}

You just need to add that method to your base test case class, and you will then be able to use @requires WordPress multisite instead of messing with markTestSkipped() all the time. For tests that should only run when multisite isn’t enabled, you can use @requires WordPress !multisite.

You could easily add more options for any other requirements your tests commonly have.

Travis CI, Composer, and PHP 5.2

Once I’ve written some PHP unit tests for my plugins, I like to make sure I put them to good use. I develop the plugins on GitHub, so with the right tools, it’s easy to set up Travis CI to run my tests. This will let me run the tests against all of the PHP versions I need too without the hassle of trying to do this locally.

The only problem is that WordPress still supports PHP 5.2, and while I want to run my tests against that version, I’m using composer to install some of my dev dependencies. And as you probably know, composer requires PHP 5.3. So I searched around the internet to see if anyone had a solution to this dilemma. I did find one project on GitHub, but it requires you to have a separate config file for PHP 5.2, and doesn’t appear to be maintained at this time.

What I was really hoping for was a way to run composer using PHP 5.3 even when the tests are running on 5.2, since all of the PHP versions are installed on the Travis test box. I couldn’t find any helpful information about switching PHP versions on Travis, but with a little research into phpenv (which Travis uses to manage the PHP environment), I was able to figure something out.

It’s actually as easy as this:

phpenv global 5.3
composer install
phpenv global "$TRAVIS_PHP_VERSION"

Just drop that into the before_install section of your .travis.yml, and you’re ready to go!

Creating Your Own WordPress Unit Test Factories

WordPress has these things in its PHPUnit test library called factories. Their purpose is to allow you to easily create things, like posts.

You might wonder why that’d be so helpful, since after all, WordPress already provides functions like wp_insert_post(). If you are wondering that, maybe you haven’t written very many unit tests.

The problem with wp_instert_post() et al. is that you have to make up a lot of the post’s attributes, like its title and content. While this can be amusing, it can quickly become boring and time consuming. This is especially so when those fields don’t matter in your test in the first place.

WordPress’s solution to this is to provide these factories in its test cases. When your test case extends WP_UnitTestCase, you have access to the factory property, which is a WP_UnitTest_Factory instance. The factory itself has several properties, like post, which is a WP_UnitTest_Factory_For_Post instance.

So you can create a post just by calling $this->factory->post->create(). You don’t have to worry about the post’s attributes, because they will be generated as needed. And if you do need to set the title, for example, you can easily do that:

$this->factory->post->create( array( 'post_title' => 'My Title' ) );

There are other factories as well, for users, attachments, comments, etc. They pretty much cover everything you’d want a factory for in WordPress.

But sometimes a plugin has its own entities that it needs to create in its unit tests. WooCommerce orders, for example. This can be achieved by creating custom factories. You just need to create your own child of WP_UnitTest_Factory_For_Thing, which all of the factories extend. It has just three abstract methods that you’ll need to create: create_object, update_object, and get_object_by_id. It’s pretty simple to implement these, and they do exactly what you’d expect based on their names.

__construct()

Oh, did I forget to mention the constructor? That is actually one of the most important parts. In your constructor is where you have the opportunity to set up the default values for each of the entities’ properties. For example, in the post factory, the constructor looks like this:

	function __construct( $factory = null ) {
		parent::__construct( $factory );
		$this->default_generation_definitions = array(
			'post_status' => 'publish',
			'post_title' => new WP_UnitTest_Generator_Sequence( 'Post title %s' ),
			'post_content' => new WP_UnitTest_Generator_Sequence( 'Post content %s' ),
			'post_excerpt' => new WP_UnitTest_Generator_Sequence( 'Post excerpt %s' ),
			'post_type' => 'post'
		);
	}

The post status and post type default to scalar values, so that shouldn’t be unfamiliar to you. The really interesting part here is the WP_UnitTest_Generator_Sequences. As you can see, these are constructed with a string that contains a %s placeholder. The string will be used as the content for the default created posts, but the placeholder will be replaced with an integer. That number is from an iterator in the generator that gets incremented each time the field needs to be generated. So the first post title generated will be ‘Post title 1’ and the second will be ‘Post title 2’. This means that the generated fields will be unique, which can be especially good when debugging.

create_object()

The create_object() method is called by the higher-level methods create(), create_and_get(), and create_many(). It is passed an array of arguments, and is expected to return the ID of the object that’s created, or false or a WP_Error if it fails. In the post factory, it looks like this:

	function create_object( $args ) {
		return wp_insert_post( $args );
	}

That’s very simple, isn’t it? The $args have already been merged with the defaults we defined in the constructor before they get passed in, so all we need to do is insert the post.

update_object()

The update_object() method is called by the create() method, strangely enough. It is used to update the object’s fields after applying any callbacks that have been registered (that’s another story for another time). It gets passed the ID of the object to update, and an array of fields to update. The return value should again be false or a WP_Error object.

In the post factory it looks like this:

	function update_object( $post_id, $fields ) {
		$fields['ID'] = $post_id;
		return wp_update_post( $fields );
	}

Again, really simple. We just set the ID as one of the fields, since that’s the way wp_update_post() takes its arguments.

get_object_by_id()

The get_object_by_id() method is called by create_and_get() to retrieve the object once it has been created. It is passed an ID, and is expected to return that object.

In the post factory, it looks like this:

	function get_object_by_id( $post_id ) {
		return get_post( $post_id );
	}

There’s not much to see here either. Just retrieve the post and return it.

Conclusion

All in one piece, the post factory looks like this:

class WP_UnitTest_Factory_For_Post extends WP_UnitTest_Factory_For_Thing {

	function __construct( $factory = null ) {
		parent::__construct( $factory );
		$this->default_generation_definitions = array(
			'post_status' => 'publish',
			'post_title' => new WP_UnitTest_Generator_Sequence( 'Post title %s' ),
			'post_content' => new WP_UnitTest_Generator_Sequence( 'Post content %s' ),
			'post_excerpt' => new WP_UnitTest_Generator_Sequence( 'Post excerpt %s' ),
			'post_type' => 'post'
		);
	}

	function create_object( $args ) {
		return wp_insert_post( $args );
	}

	function update_object( $post_id, $fields ) {
		$fields['ID'] = $post_id;
		return wp_update_post( $fields );
	}

	function get_object_by_id( $post_id ) {
		return get_post( $post_id );
	}
}

You might be amazed that this little bit of code could really be so helpful, but I assure you that it is. It is fully worth creating factories for custom objects if you need to use them much in your tests. They may not all be as simple as this one, but that is only another reason to build it and keep your code DRY.

WordPress Unit Testing with WP_Http

If you use wp_remote_request() or other wrappers for WP_Http methods in your code, this makes it difficult to test, especially if the remote server may not be reachable from your testing environment. That’s why I created the WP_HTTP_UnitTestCase to solve this by letting you mock the remote responses.

The usage details are there in the repository’s readme file.

It lets you mock the remote responses coming from the server, and also captures the data that was sent to the remote server so you can check that the correct requests are made.

As I continue to write unit tests for WordPress plugins I create, I think I’ll find this helper useful. I hope you might as well.