In my last post, I showed how to modify the WordPress content endpoints using WordPress hooks — aka the WordPress plugins API — during REST API requests. The practical example changed the underlying query to improve search.
Now I need to test that code isolated from the WordPress plugins API. For this article, we will assume the Plugins API is working as documented. This, however, isn’t a safe assumption, so we need to be ready if it isn’t working as planned.
This post is really about how we can control for that problem. I’ll be walking you through setting up a WordPress plugin for acceptance testing and unit testing, and explaining what we can and can’t test with unit tests. Doing so should help you understand how to structure your code with this distinction in mind and begin to show you the tools and techniques of writing these two distinct kinds of tests.
Setting Up A WordPress Plugin For Unit & Integration Tests
Using WP-CLI To Create A WordPress Plugin With Unit Tests
I don’t want to spend a ton of time on how to setup and structure the plugin and tests. We can use WP-CLI to scaffold most of the code for us. So first, let’s create the plugin using this command.
wp scaffold plugin rest-api-search
This creates a plugin called “rest-api-search” with the WordPress test suite ready to go. This is great, because we’ll want to use that for integration tests. I’d rather use PHPUnit directly for my unit tests. Also, I’d like to manage running tests and sniffs using Composer and have Composer to provide a PSR-4 autoloader.
You can learn more about how I set this up by reading these posts from Torque:
- How to Test Emails WordPress Sends
- Don’t Follow WordPress Coding Standards
- How to Enforce Code Standards in WordPress Development Using Composer
- Why and How to use Class Autoloading and Namespacing to Improve WordPress Development
I did make two big changes to what WP-CLI created. First, I changed the PHPCS ruleset to use tabs instead of spaces in order to follow the PSR2 instead of the WordPress coding standard.
Previously I had used inline arguments for the code sniffer. Now I am using a PHPCS XML configuration file as command line options would be too complex:
<?xml version="1.0"?> <!--Largely Sourced Form https://gist.github.com/gsherwood/9d22f634c57f990a7c64--> <ruleset name="CalderaLearn"> <description>PSR2 with tabs instead of spaces.</description> <arg name="tab-width" value="4"/> <rule ref="PSR2"> <exclude name="Generic.WhiteSpace.DisallowTabIndent"/> </rule> <rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/> <rule ref="Generic.WhiteSpace.ScopeIndent"> <properties> <property name="indent" value="4"/> <property name="tabIndent" value="true"/> </properties> </rule> <!-- The soft limit on line length MUST be 120 characters; automated style checkers MUST warn but MUST NOT error at the soft limit. --> <rule ref="Generic.Files.LineLength"> <properties> <property name="lineLimit" value="120"/> <property name="absoluteLineLimit" value="0"/> </properties> </rule> </rulese
This imports the PSR-2 standard and adds one modification. To quickly confirm my code to this new standard, I used the command phpcbf, which is a part of PHPCS, to fix all of my code to the new standard. I changed the “fixes” command from the previous article to use PHPCS’ built-in fixer since it will automatically use that same xml configuration file. Here is the new command:
phpcbf src/ && phpcbf tests/
Second, I restructured the tests so that they were in two suites: unit and integration. Later on an acceptance testing suite may get added, but for now, that’s what we need. Since I’m going to use Composer’s autoloader for tests, I organized the directories and class names according to the PSR-4 spec.
As a result, the directory for my unit test is Tests/Unit and the directory for my testing mocks is Tests/Mocks. I moved the tests WP CLI generated into their own directory and bootstrap folder that are not being used. Here is what my directory structure ended up looking like before I started adding my code in:
The directory src will house my root namespace and all of my PHP classes. Here is the composer.json that pulls this all together:
Running Tests On Git Hooks
One other new thing you might have noticed was that I added cghooks — composer git hooks. This package is a Composer plugin that hooks into Git events, and lets you run Composer scripts at those events.
I used the pre-commit hook, which fires when you make a Git commit, before it is recorded. Because this script has to exit successfully for the commit to be completed, the setup is enforcing a set of standard, required pre-commit steps.
In my case, I only added the code formatting, not the tests. This prevents code formatting issues from getting pushed uupstream. But it still allows incomplete work to be pushed before it fixes all of the tests.
I am hesitant to recommend using this on a community open-source project as not all developers may want to or need to install the tests to contribute.
Preparing For Unit Tests
We’re going to have to do a little refactoring before we can really get into testing. But first, I’d like to pause and write one test first.
Let’s create a super basic test that doesn’t cover any of our code. I like to add this type of test first, just to make sure my plugin test are set up correctly. It’s just asserting true is true. Effectively, it covers that PHPUnit works for in this context.
<?php namespace CalderaLearn\RestSearch\Tests\Unit; /** * Class ExampleTest * * An example unit test * * @package CalderaLearn\RestSearch\Tests\Unit */ class ExampleTest extends TestCase { /** * Super basic test for educational purposes * * @covers TestCase */ public function testTheTruth() { //What should the value be? $excepted = true; //What is the value actually $actual = false; //Are they not the same? $this->assertNotEquals($excepted, $actual); } }
When we’re writing unit tests, our goal is to test our own systems with as few dependencies as possible. No dependencies is the impossible ideal we strive for. We’ll trust that PHPUnit’s tests work, as long as that one test passes we can trust that dependency.
You may notice that this test is extending a class in the same namespace called TestCase. That’s just an empty class extending PHPUnit\Framework\TestCase. The recommended file structure for phpunit tests is one class of tests per class. So we’re going to have several classes in our tests directory. I guarantee you at some point we’ll want to share code between these tests. Having all of our test classes extending one class is going to help a lot.
<?php namespace CalderaLearn\RestSearch\Tests\Unit; //Import PHP unit test case. //Must be aliased to avoid having two classes of same name in scope. use PHPUnit\Framework\TestCase as FrameworkTestCase; /** * Class TestCase * * Default test case for all unit tests * @package CalderaLearn\RestSearch\Tests\Unit */ abstract class TestCase extends FrameworkTestCase { //We'll put shared code for all tests here later }
Time To Start Testing
If that test passes, then you have your test suite setup. That’s awesome. Now you can start writing tests on your own, or wait until the next post were we’ll cover refactoring the code for testability and writing more tests.
The plugin I’ve been using to create and test this example code can be found on Github. It needs more tests to achieve full coverage. Feel free to fork it and learn more by experimenting with it.
Before we wrap up, I’d like to come back briefly to the automatically generated same test that WP-CLI generated and it’s install script. WordPress’ test suite, which we’ll get back to, is not going to be run with composer like the rest of our tests are.
It is common in the WordPress ecosystem to use that test suite for unit tests and integration tests. I’m not fond of that pattern, as I want to treat them as two totally different test suites and I like using Composer for scripting all of this. I’ll be covering why over my next two posts, but I want to point that out if you’re used to using them together.
No Comments