# Introduction to WordPress unit testing

*I gave a talk at [WordCamp Toronto 2015](https://toronto.wordcamp.org/2015) on WordPress unit testing. This is the companion article that I wrote for it. If you're just looking for the slides, [click here](/introduction-wordpress-unit-testing/#slides).*"But this worked the other day!" I don't know about you, but reading that sentence just frustrates me! Don't you hate when things worked one day and it doesn't the next. It's the stuff of (developer) nightmares. What if there was a way to ward yourself against that evil? Well, you're in luck because that's the goal of [software testing](https://en.wikipedia.org/wiki/Software_testing)! It prevents this situation from happening over and over again. On top of that, it helps you improve the quality of the code you write. Awesome! Now, software testing is a HUGE field. You have a ton of different types of testing. Each with its own purpose (and, sometimes, philosophy). Today, we're going to focus on just one type of testing. It's called [unit testing](https://en.wikipedia.org/wiki/Unit_testing). # So what is unit testing?

Well, in the full spectrum of testing, unit testing focuses on testing at the smallest scale. In most cases, that means inputting values into a function (or object method). You then check at what comes out at the other end. "Checking what comes out at the other end" tends to boil down to two things. You can verify that the function returned the proper value. Or you can look for errors when you supply invalid input. The ultimate goal is to ensure that the behaviour of our function stays consistent. You can make changes to it knowing that there's a safety net that'll catch you if you make a mistake. This ends up improving the quality of your work in the long run. ## The importance of isolation in unit testing

Most types of testing need some amount of isolation. That said, nowhere is this more important than in unit testing. Unit testing, by its name, is about isolating a unit of code for testing. Isolating a unit of code is a lot like hooking your function to a set of magical machines. They let you control everything that surrounds your function. In the testing world, we call these machines [test doubles](https://en.wikipedia.org/wiki/Test_double). For example, let's say that your function calls `apply_filters`. A test double can check if your function passed it the correct value. It can also return a value that you choose to your function. This lets you focus on just testing the internal logic of the function. You don't have to worry about what the rest of the application (WordPress here) is doing. It's all under your control. ## What makes testable code higher quality

Now, this doesn't mean that you can't have high-quality code without tests. Of course, you can. But why is it that you often hear something like "Testable code is higher quality code." The answer lies with something called [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity). It's a metric that evaluates how complex a function is. It does so by calculating how many independent paths there are through your code. We won't be diving into it more than that since it's just a lot of math (boring!). That said, it has an important takeaway that applies here. In general, the larger a function gets, the more complex it becomes. And the more complex a function is, the harder it is to test it. Here's an example. Let's say you have a function that has hundreds of lines of code. It has plenty of conditional statements. That means that you have dozens of possible paths through it. Next, imagine that you had to create tests for all these paths. The tests themselves need to be more complex. That's because your function might call a bunch of other functions as well. All these factors turn isolating your code and controlling the inputs into a nightmare. This pushes you towards writing smaller, less complex functions. These functions are easier to isolate and need fewer inputs. They also tend to be of higher quality. That's because [they focus on doing a specific job](https://carlalexander.ca/single-responsibility-principle-wordpress/) and doing it well. ## The difference between unit tests and WordPress tests

Often, you'll see WordPress tests called WordPress unit tests. This isn't quite right. In the testing spectrum, WordPress tests are what we call [integration tests](https://en.wikipedia.org/wiki/Integration_testing). The point here isn't to argue semantics. There's an actual difference between the two that's worth highlighting. It's their relationship with isolation. Integration tests don't care that much about isolating a piece of code. They only care about how various pieces of code work together as part of a larger whole. You might need to use isolation to do that, but it isn't necessary most of the time. That's all well and good, but which of the two is better then? Neither is better than the other. In fact, the best is to do both. You start by using unit tests to test the behaviour of your code. You then use WordPress tests to test how your code behaves within WordPress. That said, this is a topic for another time. You'll see that you already have plenty on your plate with unit testing. Let's get started. # The unit testing setup

Before we start building our first unit tests, we need to make sure that you have the right setup. This next section will go over all that. We'll go over the requirements and the tools necessary for running unit tests. ## Operating system

Let's start with the operating system. A lot of the tools that we'll use are only available on UNIX-based operating systems. That's another way to say anything, but Windows. But all isn't lost for you Windows users out there! You can still write unit tests for your plugin. You'll just need a bit of help. You can use a virtual machine to run the tests on your computer. You can also use an automated testing service to do run them for you. ## Required PHP versions

To create test doubles of WordPress functions, we need to use [namespaces](https://secure.php.net/manual/en/language.namespaces.rationale.php). This means that your plugin will need to use PHP version 5.3 or later. We're also going to use a second testing library besides PHPUnit. This testing library uses a [trait](https://secure.php.net/manual/en/language.oop5.traits.php) to add functionality to our tests. This will limit the coverage of your automated tests (a topic for another time) to PHP 5.4 and later. ## Composer

We'll need to use [composer](https://getcomposer.org) for our testing setup. Composer is a package manager for PHP. It lets you specify external libraries that your plugin needs to work. A lof of plugin developers use it to download external libraries that they use for testing. This is also what we'll be doing. We'll use it to install this secondary testing library. If you've never installed Composer before, you can follow the installation instructions [here](https://getcomposer.org/doc/00-intro.md). ## PHPUnit

[PHPUnit](https://phpunit.de/) is one of the testing library used in the PHP world. For a long time, it was also the only option available. While it's not anymore, it's still the testing library used by a lot of PHP projects such as WordPress. If you've never installed PHPUnit before, you can follow the installation instructions [here](https://phpunit.de/manual/current/en/installation.html#installation.composer). ## WP-CLI (optional)

[WP-CLI](http://wp-cli.org) is the WordPress command-line tool developed by the community. This isn't a strict requirement for unit testing a plugin. We're just going to use WP-CLI to speed up some tasks around our unit tested plugin. If you've never installed WP-CLI before, you can follow the installation instructions [here](http://wp-cli.org/#install). # Creating our unit tested plugin

We've got our unit testing setup ready. It's time to get to work building our unit tested plugin. ## Create our plugin

First, we need to create our plugin. The easiest way to do that is with WP-CLI. You just need type in one command. ```
wp scaffold plugin unit-tested-plugin --plugin_name="WordPress Unit Test Demo"
```

This is all done using the `scaffold plugin` command. It creates our plugin with all the necessary plugin files to get going. On top of that, it'll generate a lot of the files that we need to run our unit tests. ## Add composer.json file

WP-CLI doesn't create the `composer.json` file for our plugin. This JSON file describes our plugin to Composer. We only need this file to tell Composer what external libraries our plugin wants to use. ```
{
    "name": "carlalexander/wordpress-unit-test-demo",
    "type": "project",
    "description": "Demonstrates how to build unit tests with WordPress",
    "homepage": "https://carlalexander.ca",
    "license": "GPL-3.0+",
    "require": {
        "php": ">=5.3.0"
    },
    "require-dev": {
        "php-mock/php-mock-phpunit": "^0.3"
    }
}
```

Here's our `composer.json` file. For the sake of completeness, there's a bit more than just our requirements in it. The first part of the file describes our plugin a bit. We give it a name, a description and a license. The two entries that we care about are `require` and `require-dev`. `require` tells Composer the general requirements of our plugin. For our plugin, that's just the PHP version. As we discussed earlier, we need PHP 5.3 to use namespaces in our plugin. `require-dev` tells Composer the development requirements of our plugin. These are extra requirements on top of the ones in `require`. This is where we'll add the requirement for our secondary testing library: PHP-Mock. This is why we need Composer in our setup. [PHP-Mock](https://github.com/php-mock/php-mock) is the library that will let us create test doubles of WordPress functions. We'll be using that library to build all our unit tests. ## Modify bootstrap.php

`bootstrap.php` is a file that PHPUnit will load before running our tests. It's where you put your extra configuration options. It's also where you want to load external libraries like WordPress. WP-CLI created `bootstrap.php` when we ran the `scaffold plugin` command. Since WP-CLI doesn't support Composer, the bootstrap file won't load anything that Composer downloaded. We need to fix that. ```
# tests/bootstrap.php

require_once(dirname(__DIR__) . '/vendor/autoload.php');

$_tests_dir = getenv('WP_TESTS_DIR');
if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';

require_once $_tests_dir . '/includes/functions.php';

function _manually_load_plugin() {
    require dirname( __FILE__ ) . '/../unit-tested-plugin.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );

require $_tests_dir . '/includes/bootstrap.php';
```

We added that `require_once` statement at the top of the file. It tells PHPUnit to load the [autoloader file](https://secure.php.net/autoload) created by Composer. This is what will let us use the PHP-Mock library (and any other external libraries) in our tests. ## Adding our first plugin function

We're almost ready to create our unit test. There's only one problem left. It's that we no code to test! Our plugin file sits empty in our plugin directory. We need to rectify that situation. ```
# unit-tested-plugin.php

namespace UnitTestDemo;

/**
 * Get a plugin option from the WordPress database.
 *
 * @param string $name
 *
 * @return mixed
 */
function demo_get_option($name)
{
    return get_option('demo_' . $name);
}
```

You'll notice that we added a namespace at the top of the file. This is a necessary step to create our unit tests. Without it, we can't create our test doubles. `demo_get_option` is a helper function around `get_option`. It helps us namespace our plugin options. That way, we don't have worry about adding the `demo_` prefix all the time. # Creating your first unit test

Cool! Now that our plugin is ready for testing. It's time to create our first unit test. ## Creating our empty test class

The first thing we need is a class to contain our tests. That's how testing works with PHPUnit. You group all your relevant tests together inside the same class. ```
# tests/test-demo.php

namespace UnitTestDemo;

use phpmock\phpunit\PHPMock;

class DemoTest extends \PHPUnit_Framework_TestCase
{
    use PHPMock;

    // ...
}

```

There are a few things about our `DemoTest` class that's worth discussing here. First one is that we're using the same `UnitTestDemo` namespace as our `demo_get_option`. This keeps our example a bit simpler. There's also the `use phpmock\phpunit\PHPMock;` statement. It [imports](http://php.net/manual/en/language.namespaces.importing.php) the `PHPMock` trait into our namespace. This will let us use it in our test class. Speaking of our test class. Our `DemoTest` class isn't extending the`WP_UnitTestCase` class. That's what you'd see in the usual WordPress test examples. Instead, we're extending `PHPUnit_Framework_TestCase` which is the default class used with PHPUnit. The `\` in front of `PHPUnit_Framework_TestCase` signifies that it's in the global namespace. There's also `use PHPMock;` statement at the beginning of our class. That's the trait from the PHP-Mock library. A trait is a way to add extra functionality to our class. It contains the function that we'll use to create our test doubles. ## Testing demo\_get\_option

Let's move on to writing a unit test for `demo_get_option`. I want to test that, if I request an option, I get the right value back. So let's do that. ```
# tests/test-demo.php

namespace UnitTestDemo;

use phpmock\phpunit\PHPMock;

class DemoTest extends \PHPUnit_Framework_TestCase
{
    use PHPMock;

    public function test_demo_get_option()
    {
        $this->assertEquals('bar', demo_get_option('foo'));
    }
}
```

`test_demo_get_option` is our test function. Prefixing our method with `test` flags our method as a test that PHPUnit needs to run. The second part of the method name is a description of the test that we want to do. `assertEquals` is how we'll check that we get the right value back. PHPUnit calls these types of methods [assertions](https://en.wikipedia.org/wiki/Assertion_(software_development)). PHPUnit [supports a lot of different assertions](https://phpunit.de/manual/current/en/appendixes.assertions.html). The first argument of `assertEquals` is `'bar'`. That's the value we expect to get back. The second argument is our call to `demo_get_option` with the argument `foo`. This fetches the `demo_foo` option from the test database. Let's try to run our test and see what we get. ![Screen Shot 2015-10-01 at 4.10.01 PM](https://carlalexander.ca/wp-content/uploads/2015/10/Screen-Shot-2015-10-01-at-4.10.01-PM.png)Oops, it looks like our test is failing! Why is that? It's because we never created our test double for `get_option`. Right now, it uses the `get_option` function from WordPress. Since there's no `demo_foo` option in our test database, `get_option` can't return `'bar'`. Instead, it returns its default value which is `false`. That's why the error says: "Failed asserting that false matches expected 'bar'." Let's fix that! ## Creating our test double

The normal reaction to our error is to create a `demo_foo` option in our test database. That would let us test that `demo_get_option` is working. That said, we won't be doing that. Instead, we're going to create a test double. This will let us test the code inside `demo_get_option` in isolation. ```
# tests/test-demo.php

namespace UnitTestDemo;

use phpmock\phpunit\PHPMock;

class DemoTest extends \PHPUnit_Framework_TestCase
{
    use PHPMock;

    public function test_demo_get_option()
    {
        $get_option = $this->getFunctionMock('UnitTestDemo', 'get_option');

        $this->assertEquals('bar', demo_get_option('foo'));
    }
}
```

`getFunctionMock` is the method that `PHPMock` trait imported into our `DemoTest` class. It takes two arguments: our `UnitTestDemo` namespace and the name of a function. Here, we want it to create a [mock](https://en.wikipedia.org/wiki/Mock_object) of the `get_option` function. So `get_option` is our second argument. Mocking functions or methods isn't something we've talked about so far. A mock (or mock object) is a specific type of test double. Its purpose is to simulate the behaviour of an existing function or object. This lets us verify how our function or method interacts with them. This is the only type of test double that allows us to do this type of verification. ```
# tests/test-demo.php

namespace UnitTestDemo;

use phpmock\phpunit\PHPMock;

class DemoTest extends \PHPUnit_Framework_TestCase
{
    use PHPMock;

    public function test_demo_get_option()
    {
        $get_option = $this->getFunctionMock('UnitTestDemo', 'get_option');
        $get_option->expects($this->once())
                   ->with($this->equalTo('demo_foo'))
                   ->willReturn('bar');

        $this->assertEquals('bar', demo_get_option('foo'));
    }
}
```

This is what our configured `get_option` mock looks like. We used three different methods to configure the expected behaviour. Let's go over them. First, we have `expects($this-&gt;once())`. It tells the `get_option` mock that we expect `demo_get_option` to call it once. When it does call it, it'll pass `demo_foo` as the option name. That's what `with($this-&gt;equalTo('demo_foo'))` checks. It validates that `demo_get_option` prefixes option names with `demo_`. The last thing we did is configure the return value. `willReturn('bar')` tells our `get_option` mock to return `'bar'`. This will only happen if `demo_get_option` called `get_option` with the correct value(s). ![Screen Shot 2015-10-01 at 4.10.32 PM](https://carlalexander.ca/wp-content/uploads/2015/10/Screen-Shot-2015-10-01-at-4.10.32-PM.png)Congratulations! You have your first passing unit test. That said, we won't leave it at that. # Pushing things further

It's common for plugins and themes to store an array inside an option. But this expectation of receiving an array can be deceitful in practice. You'll code always expecting an array which often means that your code will break if you don't get one. That's why you'll often see `(array)` next to `get_option` in those situations. This [casts](https://secure.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting) the value returned by `get_option` as an array. Instead of having to think of casting our option each time, we'll get `demo_get_option` to do it for us. We'll work at it backwards this time. We'll start by creating a test that fails with our expected outcome. Using that failing test, we'll change our `demo_get_option` function. This will give you a taste for [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development). ## Adding a default value to demo\_get\_option

But first, we'll need to make some changes to `demo_get_option`. That's because our first pass at it didn't include support for a default value. We'll need it to create our improved function. ```
# unit-tested-plugin.php

namespace UnitTestDemo;

/**
 * Get a plugin option from the WordPress database.
 *
 * @param string $name
 * @param mixed  $default
 *
 * @return mixed
 */
function demo_get_option($name, $default = null)
{
    return get_option('demo_' . $name, $default);
}
```

As you can see, we added `default` as a second argument. To change things a bit, we made the default value `null` instead of `false`. We pass that value into `get_option` as a second argument. ## Updating our first test

We also need to make changes to our first test. We now have a second argument to verify using the `with` method. Let's make the change. ```
# tests/test-demo.php

namespace UnitTestDemo;

use phpmock\phpunit\PHPMock;

class DemoTest extends \PHPUnit_Framework_TestCase
{
    use PHPMock;

    public function test_demo_get_option()
    {
        $get_option = $this->getFunctionMock('UnitTestDemo', 'get_option');
        $get_option->expects($this->once())
                   ->with($this->equalTo('demo_foo'), $this->identicalTo(null))
                   ->willReturn('bar');

        $this->assertEquals('bar', demo_get_option('foo'));
    }
}
```

You'll notice that we're using `identicalTo(null)` and not `equalTo(null)`. There's an important difference between the two. `equalTo` does a check using `==` while `identicalTo` does its check using `===`. `===` is what we call a [strict comparison operator](https://secure.php.net/manual/en/language.operators.comparison.php). This will prevent our test from passing with incorrect values. That's because values like `false` or `''` are equal to `null` when using `equalTo`. ## Creating a failing test

Now that we've updated our `demo_get_option` function, we can create our failing test. Our test will check the behaviour of `demo_get_option` with an array as a default value. We want it to always return an array in that situation. ```
# tests/test-demo.php

namespace UnitTestDemo;

use phpmock\phpunit\PHPMock;

class DemoTest extends \PHPUnit_Framework_TestCase
{
    use PHPMock;

    public function test_demo_get_option_casts_array()
    {
        $get_option = $this->getFunctionMock('UnitTestDemo', 'get_option');
        $get_option->expects($this->once())
                   ->with($this->equalTo('demo_foo'), $this->identicalTo(array()))
                   ->willReturn('bar');

        $this->assertEquals(array('bar'), demo_get_option('foo', array()));
    }
}
```

You might have noticed that `test_demo_get_option_casts_array` is almost the same as `test_demo_get_option`. That's often the case when unit testing. The difference between tests comes down to changing the inputs, outputs and expected values. So what changed here? Well, we changed the default value for `demo_get_option` to `array()`. We also updated our `get_option` mock to reflect that change. That said, our mock still returns `'bar'`. This simulates a situation where `get_option` found an option in the database. The problem is that the returned value isn't an array. This is the critical aspect of our test. We don't want to get `'bar'` back from `demo_get_option`. We want to get `array('bar')` even if the value from `get_option` was `'bar'`. That's what `assertEquals` is looking for. ![Screen Shot 2015-10-01 at 4.27.25 PM](https://carlalexander.ca/wp-content/uploads/2015/10/Screen-Shot-2015-10-01-at-4.27.25-PM.png)Right now, this doesn't work. `demo_get_option` just returns the value returned by `get_option`. It doesn't consider that we might not want it as is. We need to change that. ## Smart-casting an option as an array

To fix our failing test, we need to compare the value we get from `get_option` with our `default` value. ```
# unit-tested-plugin.php

namespace UnitTestDemo;

/**
 * Get a plugin option from the WordPress database.
 *
 * @param string $name
 * @param mixed  $default
 *
 * @return mixed
 */
function demo_get_option($name, $default = null)
{
    $option = get_option('demo_' . $name, $default);

    if (is_array($default) && !is_array($option)) {
        $option = (array) $option;
    }

    return $option;
}
```

This is what our updated `demo_get_option` function does now. We store the value from `get_option` in the `option` variable. We then compare it to our `default` value. To do that, we use [`is_array`](https://secure.php.net/manual/en/function.is-array.php) to first detect if `default` is an array. If it is, we check the value returned from `get_option` as well. If that value wasn't an array, we cast it as an array. This ensures that our option is always an array when we pass a default value that is an array. ![Screen Shot 2015-10-01 at 4.27.59 PM](https://carlalexander.ca/wp-content/uploads/2015/10/Screen-Shot-2015-10-01-at-4.27.59-PM.png)And there you go! Both our tests are passing now. # Building your testing habit

At this point, you might be thinking that this was a lot of work for a debatable benefit. That's a common feeling when you start writing tests. A test, by itself, feels pretty insignificant. But you have to keep the big picture in mind. That test is just one loop in your safety net. You need a lot of them before you can feel like there's something there to support you. That's why testing is a lot like picking up a new healthy habit. It's hard to stay motivated when you start. (Chips are soooo tasty!) There seems to be an endless source of seductive objections looking to convince you not to do it: - "Why write tests when I could write cool code instead!?"
- "There's no way this is going to break!"
- "We're going to miss this deadline if we write tests."

You'll have to overcome those to develop your testing habit. If you're ready to start, you can use the code from today as your base. It's all available in this [GitHub repo](https://github.com/carlalexander/wordpress-unit-test-demo). # Slides

Here are the slides for the [talk I gave](https://toronto.wordcamp.org/2015/session/introduction-to-wordpress-unit-testing/) at WordCamp Toronto 2015. https://speakerdeck.com/carlalexander/introduction-to-wordpress-unit-testing