Unit Testing WordPress Plugins

I’ve just got to say it, I hate Unit Testing. I’m not opposed to it, I see its value, and I don’t think TDD is dead. I just think that it’s a huge pain in the butt to do correctly. If it weren’t for my friend and PHP Unit Testing guru Chris Hartjes , I would probably still be “testing” by simply adding var_dumps() into my code until I found out what was going wrong. Then hope and pray that once it’s fixed, it stays fixed.

Thankfully though I do have a lot of friends who practice testing and it has rubbed off on me. So when I decided to write SimpleNonce, I decided it would need unit tests. I dove down the rabbit hole head first.

 

Prerequisites

Before we get started, you will need a few things.

  1. You will need a working copy of WordPress. Something NOT in production. Now, I will say, this is not strictly necessary for Unit testing but if you are writing your plugin, you are going to want this for obvious reasons. However, if your plugin is done and stable, you don’t actually have to have a development environment to do Unit Testing. 

    This is normally the place where I encourage you to create a Pantheon sandbox to play with. You can certainly use a Pantheon sandbox to develop your plugin. When you get ready to unit test it though, you will need shell access and that’s not something you can get on Pantheon.

  2. A plugin to test. This post doesn’t talk about writing a plugin, just testing one. If you are interested in testing, you probably already have a plugin to test. If not, clone or fork SimpleNonce. It’s the simplest piece of code you can have and still call it a plugin. You can play with it.

  3. PHPUnit. This is a must. There are several ways to Install PHPUnit , I chose to install the PHAR in my /usr/local/bin so that all my projects could use it, not just SimpleNonce. You set it up the best way for you. The end result is that you need to be able to type phpunit into a shell window and get the help screen.

  4. wp-cli. Ok, this isn’t strictly necessary but it is very handy. wp-cli will setup your testing scaffolding and even create a sample test for you. The end result is after you run wp-cli, you can immediately run phpunit in your plugin directory and see a test pass. (a very simple test, but a test none-the-less)

  5. Access to a MySQL database.

Once you have all five pre-requisites, we are ready to dive in.

 

Shaving the Yak

Setting up PHPUnit has always been the stumbling block for me. Even with simple PHP projects, setting it up was challenging. With WordPress it’s even more of a challenge, because to test a plugin, you have to have an instance of WordPress setup. Since you don’t want to destroy your database - even your development database - every time you run your tests, you will need a different setup. This means you have to setup and teardown WordPress and the database, every time you run your tests. You can see where this would get to be a pain. Thankfully, if you are using wp-cli, they’ve got you covered.

In your plugins directory execute this command:

$ wp scaffold plugin-tests

Note: If you used wp-cli to create the scaffolding for your plugin, you can skip that step; the necessary files and directories have already been created.

This just created four things:

  1. bin/wp-install-tests.sh 
    This is the magic that makes it possible to test plugins. This shell script will install WordPress for you in /tmp/WordPress. (Honestly, I have no idea where it puts it on Windows, or if it will even work on Windows.) It does more than that; it downloads some additional pieces for testing, massages a few files to populate them with appropriate values, creates a database, etc. We will come back to this one because we can use it to do some fun stuff.

  2. tests/test-sample.php 
    This is your first test. Once everything is properly setup, you can run phpunit to execute this test. If you do, you should see a green bar with one test and one assertion passing. This just lets you know that everything is setup properly. You can be lazy and rename this file, then use it as your first test for the plugin. Alternatively, you can just delete it and start from scratch. (Guess which one I do?)

  3. tests/bootstrap.php 
    This is where you set things up for YOUR plugin. You include the main file, you do anything else necessary to get things running. Remember that you are not running your tests in your development version of WordPress, you are running it in a special version elsewhere on your filesystem. So take that into account.

  4. phpunit.xml 
    This is the manifest file for PHPUnit that tells it how things are set up and what to test.

Before we can do anything though, we need to set up our testing copy of WordPress. As I said, wp-cli gave us the tool to do this, but we’ve got to execute it. From the root directory of your plugin, enter this command.

$ sh bin/wp-install-tests.sh

You will notice immediately that it does not work, but tells you you need to execute it with a few required parameters. The ones enclosed in “<>” are required. The ones enclosed in “[]” are optional.

Execute it again with the required parameters (the database connection information) and you will see it create your test infrastructure. The ONLY thing not executing out of the test WordPress you setup will be your plugin that you are testing. That executes from where it is located.

If everything worked correctly, you should be able to execute phpunit and see the PHPUnit green bar showing that one test passed and one assertion passed. CONGRATULATIONS!

If you see a red bar indicating a failure, go back and check what you did; you missed something along the way.

 

Do the Deed

OK, time to write your first test. While this article is not a primer on PHPUnit, we do have to cover it a little bit. Here is the test for SimpleNonce.

As you can see, since it is a very simple plugin; we have 5 tests and 17 assertions testing out our code.

testSimple()
This just tests the basics. Does the plugin produce a NONCE and does it validate it? Nothing more. If this one fails, we’ve broken something horribly as this method is the heart of the code.

testFormField()
Just what the name says, this test makes sure that we create a proper form field. We use the assertRegExp() to test that the field we created has the proper value in the value property.  Then we use preg_match() to pull the value out of the field and test that it validates properly.

testComplex()
This test creates several NONCEs before starting to validate them. It tests the system’s ability to keep multiple NONCEs straight.

This test is a “regression test”. Early on, the system had a problem in which it wasn’t properly identifying a NONCE if another one had been created by the same user first. Once I identified that there was a problem, I wrote a test to test it. Once that test passed, I knew I had solved the problem. Since all my other tests were also passing, I knew I didn’t break anything in the process of fixing the problem. Regression tests are important to ensure that you maintain integrity throughout your system while bug fixing or refactoring. Every bug you identify should have a test written for it first, so that you know that you’ve fixed it.

testUsedOnce()
Since “Used Once” is a core concept to NONCE, we need to test to make sure that our NONCEs can only be used once. We do this by creating one, validating it, and then attempting to validate it again. The first validation is checked with assertTrue() since it should validate properly. The second one is checked with assertFalse since it should fail if everything is working properly.

testCleanup()
There will be times when a NONCE is created but never validated. Left unchecked, this will clutter up your wp_options table. wp-simple-nonce has a cleanup routine that runs once a day that deletes any NONCE older than 24 hours from the database. testCleanup() tests that routine to make sure it works properly. The cleanup function, clearNonces(), takes a single parameter. If you pass in a true, it will force the deletion of ALL NONCEs. If you don’t pass anything in, or you explicitly pass in a false, it will only delete the NONCEs that are older than 24 hours.  

 

Conclusion

Testing is important. It is important to make sure that you write code that does what it is supposed to do. It is important to make sure you don’t re-introduce bugs into your system. It is important to be able to prove that you have a stable system ready for deployment at any given moment.

The problem with testing is not that it’s not important; the problem is that it’s hard. Many developers, and a lot of upper management of the Pointy Haired Boss variety, see testing as unnecessary overhead. Those developers have yet to hit the wall where you introduce a bug in module B by refactoring module A of your system. I guarantee you that once you have hit that wall, you will preach the testing gospel.

My hope with this article is that I can inspire you to start writing tests for your pluginsbefore you hit that wall.

Topics WordPress