The Beginner’s Guide to Unit Testing: Building a Testable Plugin

In the first part of this series, we took a high-level look at testing methodologies and gave some cases as to why it's beneficial for us to begin doing in our WordPress projects. We also took time to setup PHPUnit and the WordPress Tests in order to begin building our first testable plugin.

In this final article, we're going to define a methodology for unit testing, begin incorporating it into our work, and walk away with a fully functional (albeit simple) plugin that also has a small set of tests to ensure that it works exactly as expected.


A Unit Testing Methodology

When it comes to testing, there are generally two ways to do it:

  • Write your tests, then write code to make your tests pass
  • Write your code, then write tests that pass

In my experience, the first approach is always better. Granted, this is practically impossible to do within the context of an application that already exists, but if you're starting from the ground up – which we are – it's a better approach and here's why: Once you've written an application, you know how it works. As such, it can be extremely difficult to write tests that stretch the application when you inherently know how it's supposed to function.

To that end, I find it better to write the tests first. This way, your tests not only include the way the program is supposed to work, but it also becomes a form of documentation showing what functionality is intended and will ultimately yield a failure when the functionality isn't performing as it should.

With that in mind, we're going to be building with this simple methodology:

  • Write a test and run it. It will obviously fail.
  • Write code that attempts to cause the test to pass.
  • If the test passes, we move on to the next function; otherwise, we repeat the process until it does pass.

Finally, as a refresher, our plugin is going to give a specialized welcome message to the visitor based on if they've clicked through to the site from either Google or Twitter. We'll also be writing this in such a way that it will be easier to expand with additional services, should you want to do so in the future.


Building a Testable Plugin

At this point, it's time to start writing some code; however, unlike most projects, we're not going to be jumping into WordPress-specific code just yet. Instead, we're going to stub out our unit test class. If you've structured your plugin's directory based on what we shared in the first post or how we've configured it on GitHub, then you should have a hello_reader_tests.php file located in your tests/wordpress-tests directory. You don't have to follow that organization, of course, but it will help as we progress through the project.

Let's stub out the unit test class:

Now, attempt to run the test using from the terminal using PHP unit. Assuming that you're running PHP unit from your local MAMP installation, you should be able to enter:

$ /Applications/MAMP/bin/php/php5.3.6/bin/phpunit ./hello_reader_tests.php

At this point, you should see a failure:

Failing Tests

That's good! It means PHPUnit is installed and running and that your WordPress Testing framework is ready to go. The test failed simply because we haven't actually written any tests. Let's get started doing that.

Our First Test

First, let's write a test to make sure that our plugin is initialized, instantiated, and ready for testing. Recall earlier in the first article that we stored a reference to the instance of Hello Reader into the PHP $GLOBALS array. This is how we'll be accessing that instance using the testing framework. So let's update our unit test to look like this:

Note that for the sake of space, I'll be leaving out code comments but the fully commented plugin and tests will be available on GitHub.

Above, we've setup a reference to the instance of the plugin so that we can access it throughout our unit tests. We're using the setUp method to grab the reference to the plugin from $GLOBALS. Note, however, that we've introduced another function called testPluginInitialization. This function verifies that the reference we've setup in the setUp method is not null.

If you rerun the tests, you should now get a passing test and your terminal should look like this:

Passing Tests

There is an important takeaway here: Note that the single function we've provided above has a clear purpose: to verify that the plugin has been properly initialized. Its function name is clear and contains a single assert statement. This is a great way to model our remaining tests primarily because it makes it easy to find bugs when they appear. Think about it this way: If you include a number of different assert statements in a single function, it's going to be difficult to determine which assert statement is failing.

The First Function

Now that we've gotten introduced to how to write unit tests, run unit tests, and evaluate how they pass or how they fail, let's begin implementing functionality for the plugin. First off, we're going to need to setup a filter for the content since we're going to be appending text to the beginning of the content. In following with the methodology that we defined earlier in this article, let's write our test first.

This particular test is going to look to see if we've appended a specific set of text to the first part of the post:

If you run the test exactly as it is, it won't even fail – instead, PHPUnit will return a fatal error because the method isn't defined in the plugin. So let's add that now. Update the plugin to look like this:

Now attempt to run the test. The test won't bomb, but you should actually see a failure along with a clear message as to why the test failed:

So, in keeping with our methodology, we want to make this test pass. In order to do so, we need to make sure that the post content contains the string of text – in this case, 'TEST CONTENT', in order to make it pass. So let's try this. Update the corresponding function in the plugin to append the string before the content:

And again, we rerun the test only to see that it fails. If you notice our test, this is because it's looking to see of our content equals the 'TEST CONTENT' string. Instead, we need to make sure that the string starts on the content. This means that we need to update our test. Luckily, PHPUnit has an assertContains function. So let's update our code to use it:

Once again, rerun the test and you should see that the test now passes. Awesome! Now we need to write customized messages for people coming from Twitter and people coming from Google.

Welcoming Our Twitter Visitors

There are a number of different ways that we can check to see how a user has arrived at a given page. Sometimes we can check the values in the $_GET array, sometimes we can interrogate the $_SERVER array, or sometimes we can check a user's session. For purposes of this example, we're going to be looking for 'twitter.com' to be found in the $_SERVER['HTTP_REQUEST']. I say this just so you guys can follow along with what we're doing in the code.

So, generally speaking, the add_welcome_message should check to see if the request has come from Twitter and then tailor the message appropriately. Since we're in the business of testing each piece of functionality, we can write a function that can evaluate if the request is coming from Twitter. So let's write a new test:

In the plugin:

In the test:

We're obviously spoofing the HTTP_REFERER value, but that's okay for purposes of this example. The point still remains: run the test, it will fail, and so we'll need to implement the function in the plugin to have it pass:

Rerunning the test should now result in a passing test. But wait – we need to be complete. Let's make sure that we run a test to verify that this function fails when the referrer is not from Twitter.

Notice that we've updated the HTTP_REFERER and we've changed assertTrue to assertFalse. Permitting everything else is correct, run the tests and they should pass.

Repeating the Same for Google

Providing a customized message for Google will require the same thing that we did for Twitter, that is, spoof the HTTP_REFERER and then return true or false for the helper function. So, in order to avoid sounding redundant, I'll keep this section as concise as possible. The same steps must be followed as for Twitter.

First, we stub out the helper function in the plugin:

Then we stub out the test:

Running the test as it is right now will result in a failure. So, let's implement the is_from_google() function:

And now, the test should pass. But, again, we need to be complete so let's write the failure test to assume that the function won't return true when users are coming from somewhere else:

Finally, run your tests. Permitting everything else is correct, you should have six passing tests.

Pulling It All Together

At this point, we've got all we need to begin displaying custom welcome messages to our users. The only thing is that we'll need to refactor our initial test that's checking for "TEST CONTENT." Now, we'll need to introduce tests for the following cases:

  • When the user comes from Twitter, we'll say "Welcome from Twitter!"
  • When the user comes from Google, we'll say "Welcome from Google!"
  • When the user comes from anywhere else, we won't prepend anything.

So let's remove the test we created earlier testAddWelcomeMessage in place of adding three new tests.

First, we'll add a test that checks the Twitter welcome message.

In the plugin, we'll reduce the add_welcome_message to this:

And we'll add the Twitter test, first:

At this point, this is old hat, right? Run it, the test will fail. Implement the add_welcome_message to look like this:

Run it again, and it'll pass. Next up is the Google test:

Run the test, have it fail, then update the add_welcome_message in the plugin to contain a check using the helper function we wrote earlier:

At this point, you should have a fully functional plugin that has seven passing unit tests!


Conclusion

As you can see, unit testing introduces an additional level of development but can pay off significantly in maintainable, well-organized, and testable code. As your application grows, continually running tests to verify that your projects work as expected can give piece of mind. Of course, this is but a small example of how unit testing works. Applying these practices can pay off in much larger and/or complicated projects.

Finally, you can find this plugin, the WordPress Tests, and the Hello Reader unit tests fully commented on GitHub.

Tags:

Comments

Related Articles