So today begins work on my new WordPress plugin – WPHealthTracker. Why a health-related plugin you ask? Believe it or not, I at one point was in 300-level shape… complete with the airbrushed abs. Ok, so maybe I didn’t look that great, but I was running marathons and lifting about 5 days a week. That was before the mysterious chronic pain, and several subsequent years of doctor visits, a hip surgery, and muscle injections. But if there’s one thing I haven’t (completely) lost, it’s my grit, and I’m damn determined to fix my body and get back to a healthy life – and I’d like a web-based tool to help me do that.

Let’s start this journey with a little discussion about Unit Testing. From work on my other WordPress Plugin, WPBookList, I’ve come to realize that it’s almost impossible to make just one change, and then test every other function of your plugin. At some point the project just gets too large, and you’ve got to rely on something other than your coding discipline and attention-to-detail. Apparently, this is where Unit Testing can help.

Essentially, Unit Testing consists of simulating each function of your code to make sure it spits out the expected result. Yeah, it sounds like a lot of additional work, and admittedly, I’ve just begun writing WPHealthTracker with Unit Testing in mind, so ask me exactly how much additional work it really is in a couple months… my hope though is that it’ll save me a lot of hassle and headache in the long run, not to mention user frustration at the inevitability of something breaking or working differently each time an update is released… it’s like a game of coding Whack-A-Mole…

Getting Started…

First I installed PHPUnit – pretty much the standard for Unit Testing PHP. I opened a terminal window and typed:

php -r "copy('https://phar.phpunit.de/phpunit-6.5.8.phar', 'phpunit.phar');"

This grabbed the version of PHPUnit we want – apparently WordPress doesn’t play well with version 7 and up of PHPUnit, so I grabbed the last available version just before 7. This also has something to do with what version of PHP you’re running. You can specify which version of PHPUnit you’d like to download by simply changing the version number from 6.5.8 to whatever version you want. Here’s where all the versions of PHPUnit are listed.

Next type:

chmod +x phpunit.phar

to actually execute the downloaded file. After that, type

sudo mv phpunit.phar /usr/local/bin/phpunit

to move PHPUnit to where it needs to be to be accessed globally with the phpunit command. At this point, you should be able to simply type:

phpunit

in the terminal to see it’s options. Type:

phpunit --version

to verify the version of PHPUnit you just installed.

Installing WP-CLI (WordPress Command-Line-Interface)

Next you’ll need to install the WordPress Command-Line-Interface (WP-CLI). No worries though, same three steps as with PHPUnit essentially. First type:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

to get the file, then

chmod +x wp-cli.phar

to install, and

sudo mv wp-cli.phar /usr/local/bin/wp

to move.

Setting Up WordPress & Plugin Files

Now we’ll move on to setting up our WordPress install and Plugin files for Unit Testing. Change Directory to the root of your local WordPress install. For example, since the root of my WordPress install is a directory called ‘local’, I typed:

cd /Users/JakeEvans/Desktop/mamp_wordpress_doc_root/local

Now type

wp scaffold plugin-tests my-testing-pulgin

being sure to replace ‘my-testing-plugin’ with the name of the plugin you want to set up unit testing for. For example, I typed:

wp scaffold plugin-tests wphealthtracker

This command will set up the correct file structure within your plugin for Unit Testing. Next, CD into your plugin’s root directory. I typed:

cd wp-content/plugins/wphealthtracker

to get there. Now we’ll run the actual install script with this command (but wait, don’t run it yet – you’ll need to replace several things with your own info!):

bin/install-wp-tests.sh wordpress_test root 'password' localhost latest

You’ll want to replace the word root with your MySQL database username, and the ‘password’ with your MySQL database password. You can find these two bits of info in your wp-config.php file, or on the MAMP WebStart page (the link for mine happens to be http://localhost:8888/MAMP/?language=English).

You may also need to change the localhost to 127.0.0.1. Check out the MySQL Hostname in your wp-config.php file (should be something like: define(‘DB_HOST’, ‘localhost’); ) and use the value specified there. If it’s already specified as localhost and you aren’t having any success with the bin/install-wp-tests.sh command from above, then in wp-config.php, change this line: define(‘DB_HOST’, ‘localhost’); to define(‘DB_HOST’, ‘127.0.0.1);, and modify the command from above like so:

bin/install-wp-tests.sh wordpress_test root ‘root’ 127.0.0.1 latest

If this still isn’t working for you, you may need to append a port number. Open that MAMP WebStart page, and look for the section that lists your MySQL info. There should be a ‘Port’ row with the correct number. Modify the DB_HOST constant in wp-config.php like so (being sure to replace the ‘8889’ with your own port number!):

define('DB_HOST', '127.0.0.1:8889');

and modify the install command like so (again, being sure to replace the ‘8889’ with your own port number!):

bin/install-wp-tests.sh wordpress_test root ‘password’ 127.0.0.1:8889 latest

In the end, I had to change my DB_HOST constant in wp-config.php to define(‘DB_HOST’, ‘127.0.0.1:8889’);, and my install command to “bin/install-wp-tests.sh wordpress_test root ‘root’ 127.0.0.1:8889 latest” (yes, yes, I know I was using the default MySQL username and password of ‘root’ and ‘root’… like all of your passwords are unique and un-guessable!)

Once the install is complete, simply type:

phpunit

to run the default, out-of-the-box test that is located in the tests/test-sample.php file. If everything worked out, you should see a green-highlighted message saying something like: OK (1 tests, 1 assertions).

Now For Actual Unit Testing…

The actual Unit Tests themselves currently reside in the tests/test-sample.php – go ahead and open that file up and take a look – there’s not too much going on as of right now, just a class named SampleTest that extends the WP_UnitTestCase class (the class that does all the magic), and one function providing a test that will always pass.

Here’s the thing to learn from this file: see the

$this->assertTrue

line? So PHPUnit has these things called Assertions (which apparently is a concept common to all Unit Testing). There are quite a few different types (check ’em all out here), but basically, they’re tests that generally take a couple arguments and compare them to see if they satisfy that particular Assertion. The Assertion used here is the assertTrue() Assertion, which actually only takes one argument. If that argument evaluates to true, then this Assertion will pass, resulting in that green-highlighted bit of text you saw earlier. Another example would be the assertEquals() Assertion, which takes two arguments, and will pass if both arguments are equal to each other. A third example would be the assertGreaterThanOrEqual() Assertion, which again takes two arguments, and if the first argument is greater than or equal to the second argument, the Assertion passes.

Create Your Own Unit Tests

Now it’s time to create your own test file. In the test directory (where the tests/test-sample.php file is), create a new file called test-yourpluginname, replacing ‘yourpluginname’ with the name of your plugin. For example, I created a file in the tests directory called ‘tests-wphealthtracker.php’.

Here’s the basic version of how my ‘tests-wphealthtracker.php’ currently looks (after just a few hours of playing around with creating Unit Tests):

class WP_Meta_VerifyTest extends WP_UnitTestCase
{

    public function setUp()
    {
        parent::setUp();

        // Get class instance of the WPHealthTracker_General_Functions in wphealthtracker-functions.php file
        $this->functions_file_class_instance = new WPHealthTracker_General_Functions();

        // Switch doc root to the root dir of wphealthtracker
        $_SERVER['DOCUMENT_ROOT'] = WPHEALTHTRACKER_ROOT_DIR;

    }

    public function tearDown() 
    {
        // Switch doc root back to what it was
        unset($_SERVER['DOCUMENT_ROOT']);
    }

    // For testing the function that adds the admin menu entry
    public function test_wphealthtracker_jre_admin_menu()
    {
        $expected = 'toplevel_page_WPHealthTracker-Options';
        $this->assertEquals( $expected,  $this->functions_file_class_instance->wphealthtracker_jre_admin_menu());
    }

...more functions and stuff...

}

As you can see, I created a class named WP_Meta_VerifyTest, extending the WP_UnitTestCase class just like the test-sample.php file did. Both the setUp() and tearDown() functions are functions that PHPUnit calls automatically, but you can include them and add your own code as well if there are things you need to either set up, or undo, before beginning tests and after tests are complete.

I currently have one file in WPHealthTracker right now that holds several functions I want to Unit Test. In that file I’ve created a class called WPHealthTracker_General_Functions(). All of the functions I’m Unit Testing are methods of the WPHealthTracker_General_Functions class. In the setUp() function, I’ve instantiated an instance of the WPHealthTracker_General_Functions class, so that I can reference these member functions for testing. Generally speaking, in each Unit Test function, I’ll be passing two things to my Assertions for evaluation; the expected value that the member function of class WPHealthTracker_General_Functions should return (generated by my own code within each Unit Test function – think of it as a mock-up of the expected result of running that function), and the second thing I’ll pass will be a call to the member function itself, to get the actual, real-life result of the function. If the two values match or otherwise satisfy the Assertion, then the test passes!

A Simple, Real-Life Test Example

Let’s take a look at one of my tests – first we’ll look at the function in WPHealthTracker we’re wanting to test:

//Function to add the admin menu entry
public function wphealthtracker_jre_admin_menu() {
    $hook_suffix = add_menu_page( 'WPHealthTracker Options', 'Health Tracker', 'manage_options', 'WPHealthTracker-Options', array($this, 'wphealthtracker_jre_admin_page_function'), WPHEALTHTRACKER_ROOT_IMG_URL.'wphealthtrackerdashboardicon.png', 6 );
    return $hook_suffix;
}

and now we’ll check out the Unit Test function for wphealthtracker_jre_admin_menu():

// For testing the function that adds the admin menu entry
public function test_wphealthtracker_jre_admin_menu()
{
    $expected = 'toplevel_page_WPHealthTracker-Options';
    $this->assertEquals( $expected, $this->functions_file_class_instance->wphealthtracker_jre_admin_menu());
}

test_wphealthtracker_jre_admin_menu() is a Unit Test function that will check to see if the creation of the Admin Menu entry for WPHealthTracker went as expected. The first line of this function is what I’m expecting wphealthtracker_jre_admin_menu() to return – the variable $hook_suffix should contain the string ‘toplevel_page_WPHealthTracker-Options’. I know for a fact that’s what wphealthtracker_jre_admin_menu() returns, so in the test_wphealthtracker_jre_admin_menu() function, I create a variable called $expected with the value of ‘toplevel_page_WPHealthTracker-Options’. I then pass that $expected variable to my Assertion, which in this case is assertEquals(). The second argument passed to assertEquals() is a call to the actual function wphealthtracker_jre_admin_menu(), via our class instantiation we created in the setUp() function. Now I jump back to my Terminal, make sure I’m in the root directory of my plugin, and type:

phpunit

Instead of running test-sample.php, PHPUnit will run test-wphealthtracker.php. It’ll run through that setUp() function, and then run our test_wphealthtracker_jre_admin_menu() Unit Test function. As I passed an actual function call to $this->assertEquals() as the second argument, the actual wphealthtracker_jre_admin_menu() function in WPHealthTracker is run, and it’s output is returned. If that output matches the $expected variable, then the test passes.

Wrapping Things Up

This is the most basic of basic Unit Tests, but you can see how this could be useful in a large project – simply write your Unit Tests for each function in your plugin, and before you release an update just run phpunit, making sure all of your tests pass and no new bugs and/or possible issues were introduced as a result of recent changes.

Unit Testing also has the benefit of making you write better code – I’ve already had to completely re-write several of my functions to make them ‘Unit Testable’ – which basically means I’ve had to reduce the number of branching conditionals and different routes through individuals functions I may have had, to try and arrive at just one or two possible outputs for each function. Sometimes to accomplish this I’ve had to break a single function up into two or three completely new functions, which results in three smaller, easier-to-follow, easier-to-predict functions.

Anyway, that should be enough to get you started – check back in a couple months for how my thoughts on Unit Testing have changed since this post!

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *