So you have an amazing plugin, and it’s starting to get bigger and the bug reports are piling up (or better you are are starting a new plugin and you wanna do it right). Unfortunately getting PHPUnit to properly function with WordPress is a task that requires some serious configuration. Fortunately WP-CLI includes an automated method for getting started with tests in your plugin!

Using WP-CLI’s scaffold command we can automatically generate the files necessary to start testing our plugin. The most difficult portion of this process is getting everything installed properly. If you are using Varying Vagrant Vagrants which I highly recommend with Alison Barret’s Site Wizard you already have everything you need installed and you can just ‘vagrant ssh’ in and skip down to the “Setup the Plugin” section.

Installation

Install PHPUnit here http://phpunit.de/getting-started.html

Install WP-CLI here http://wp-cli.org/

Setup the Plugin

Once you have everything installed and working properly it’s just a matter of running a few cli commands.

wp scaffold plugin-tests plugin-name # replacing plugin-name with the folder name of your plugin # then move to the plugin directory if you aren't there already cd wp-content/plugins/plugin-name ./bin/install-wp-tests.sh wordpress_test mysql_username 'mysql_password' localhost latest # Replace mysql_username, mysql_password, and localhost with # the proper login credentials for your local database. # Once those command have been run you can run phpunit # Which should give you a single successful test

Writing Tests

The wp-cli scaffold command created a few new files in the plugin folder, the most important being the tests folder. PHPUnit will run any tests you place in the tests folder. Included is a file called test-sample.php in this file you can find a basic example of a WordPress PHPUnit test.

class SampleTest extends WP_UnitTestCase { function testSample() { // replace this with some actual testing code $this->assertTrue( true ); } }

In every test’s file you create, setup your test by wrapping them in classes that extend WP_UnitTestCase. Every function in the WP_UnitTestCase class that begins with test will run automatically.

Unit testing relies on assertions to confirm that the behavior your plugin is exhibiting matches the behavior that is expected. In the example above the simple testSample asserts that true is true which should always come out positive unless something is seriously wrong.

Let’s say we have a plugin that keeps track of a score for posts using post meta, let’s write a few tests to confirm the basic behavior of the plugin.

class ScoreTest extends WP_UnitTestCase { function testInitialScore() { // Create a new post $post_id = $this->factory->post->create(); // Get the new posts score $score = my_plugin_get_post_score( $post_id ); // Check to confirm the new post’s score is equal to zero $this->assertEquals( $score, 0 ); } function testIncrementScore() { // Create a new post $post_id = $this->factory->post->create(); // Increment the posts scores my_plugin_increment_post_score( $post_id ); // Get the posts score after incrementing $score = my_plugin_get_post_score( $post_id ); // The posts score should now be 1 // it started at 0 and the increment method // increase the score by 1. $this->assertEquals( $score, 1 ); // Increment the score again, this time by 2 my_plugin_increment_post_score( $post_id, 2 ); // Get the new score $score = my_plugin_get_post_score( $post_id ); // Confirm that 2 more points were added to the // post score. $this->assertEquals( $score, 3 ); } function testResetScore() { // Create a new post $post_id = $this->factory->post->create(); // Add 5 points to the post my_plugin_increment_post_score( $post_id, 5 ); // Reset the post’s score to 0 my_plugin_reset_post_score( $post_id ); // Get the post’s score $score = my_plugin_get_post_score( $post_id ); // The post’s score should be 0 because it was reset $this->assertEquals( $score, 0 ); } }

These three functions test different aspects of the post-scoring plugin’s functionality. testInitialScore creates a new post and confirms that on create the my_plugin_get_post_score function returns 0.

The testIncrementScore function confirms that the my_plugin_increment_post_score function properly increments the score by 1 — the default — or by a specified amount.

The testResetScore creates a post, gives it several points, resets its score, and confirms that the my_plugin_reset_post_score function properly returned the post’s score to 0.

With these tests in place we can easily confirm whether or not the plugin is functioning as it should, and if any changes we make to the plugin break any required functionality.

One important thing to notice in the above code is the use of $this->factory to create objects to work with. The WP_UnitTestCase class give us these methods to make it easier to test the interaction of WordPress and your plugin by giving you test data. For more information, this is a pretty nice rundown of functionality available through the factories.

Writing tests for complex functionality can be difficult, both in that the result of the function may be more difficult to confirm, and there is more space for edge cases that break in unexpected ways. If you are running into issues like these, consider breaking down large functions into smaller functions that only do one thing each so that they are simpler to test.

In addition, you can integrate writing tests as part of your bug fixing procedure. When a new bug is reported that isn’t covered by a test, write a new test that covers it so you are protected in the future.

Automation!

Once you’ve got PHPUnit up and running and have some tasks in place you can do some really cool automation. Using a task runner like gulp or grunt, you can automatically run the tests whenever a php file is saved, or even before you are allowed to do a git commit.

Grunt PHPUnit task

Gulp PHPUnit task

Further Reading

This is just a basic introduction to using PHPUnit and WordPress. For further reading on more assert methods and use of PHPUnit check out the official documentation.