In the last article, we began talking about the theory of unit testing in WordPress. Specifically, we reviewed our work on unit testing themes and plugins then began to discuss units of code, how this impacts our testing, and we reviewed unit testing in the larger world of software development.
We're going to continue discussing the theory of unit testing in WordPress, but are going to be doing so through the perspective of how it can help identify problems, drive architecture, document the project, and more.
Finding Problems, Saving Time
Recall from earlier in this series, that the traditional way of doing unit testing is this:
- Write a test, run it (knowing that it will fail)
- Write the function to make the method pass.
- Run the tests. If the test fails, continue working on the function; otherwise, move to the next.
Yes, the first step is a bit dogmatic. Why waste minutes running something you know is going to fail, right? Still, you get the idea. But as you begin to apply this particular technique to development, you're going to find that you'll develop a certain rhythm to writing your code and that's part of the whole goal.
But that's only half of it – unit testing can actually help you detect problems earlier on in development.
In order to understand, this it's probably best to back into the idea.
Let's say that you're working on a feature for a WordPress-based project where you're going to allow users to create a user account without actually logging into the WordPress dashboard. This assumes that you've got a page template setup to handle registration, the necessary validation in place, and the code for generating passwords and emails.
You load the page up in your browser, try to create a few users – some with the same email address, some with improper passwords, some with illegal characters, etc. You get the point – there are n-number of ways for the validation to pass and to fail. This is rough! This means that every single time the user registration function is changed, you have to perform the same n-number of registrations to make sure nothing is broken.
Or you can write a suite of tests to take care of it and run them all each time the code changes.
So, yes, writing unit tests can take a lot of time up front but look at the amount of time saved each time you modify a unit of code. It's well-worth it and this can help identify problems early on – that is, before it's released into production – that could have been missed because someone forgot to simulate one permutation of the test.
When it comes to writing unit tests, you're not only improving the quality of your code by ensuring that it actually works, but you're inherently providing developer-oriented documentation.
If you're unit testing the functionality that you're building into your product, you're going to be providing documentation for how functions are intended to work, when they should fail, and when they should pass.
There are a few assumptions that come with this: Specifically, that you're logically naming and grouping your functions and their associated tests and that you're properly testing each function.
Through PHPUnit, the WordPress Unit Tests make it easy to perform assertions that are easy to read. You simply state assertTrue, assertFalse, or any other assertions available on the functions that compose your project.
Following with our example above, this means that you could write a function to make sure that the user registration function fails when trying to register with an empty email address:
$this->assertFalse( registerNewUser( '' ) );
A simple example, perhaps, but the point remains: Your code becomes self-documenting and it only requires that you write clear unit tests.
Perhaps one of the most understated advantages of unit testing is that it can help drive the architecture of your project. Typically, theme or plugin development can start one of two ways:
- Listing out functions, sketching the user interface, then write code
- Draw out a diagram of how files will all work together, then write code
These aren't inherently bad, but I think they are weak (and I'll be the first to admit that I've done both more than I'd like to share!). But the "write code" step assumes a lot, doesn't it?
For anyone that's been writing code for an ample amount of time, you are all too familiar that you end up hitting that point where your realize, "Oh ... I didn't think of that."
If you're lucky, this usually means you can write a helper method or another conditional to handle the case that you neglected, but in the worst cases, it means that you may have to rework your entire class or your entire set of functions to accommodate this problem.
Unit testing, although not perfect, can help to mitigate this.
Consider the fact that, from the very beginning, you're listing out all of the functionality that you want your theme or plugin to offer. You've yet to write any code but perhaps you have some type of sketch of the UI and/or a set of class diagrams.
Next, you begin to write out the tests that you're going to be writing in order to test your project. Recall that part of unit testing is breaking down code to the most atomic unit possible, thus you're tasked with writing unit tests for each of these, ahem, units.
Because of the nature of unit testing, you're inherently thinking about your code differently: rather than "write code," you're thinking "write tests," and because you're having to think at a more atomic level, you can't help but consider fringe cases that are so often lumped into "write code."
The Language of Your Code
As developers, we're far too comfortable with using conventions that continually reinforce that we write code. By that, I mean that we tend to provide abbreviated variable names, cryptic function names, and class names that may not mean anything to anyone outside of yourself or the team that's working on your project.
Unit testing isn't necessarily the key to writing code that's easier to read, but it can go a bit further in helping to provide cleaner function names.
Recall from that first programming book you read, the first computer science class you took, or the first piece of open source code you saw, method names are typically verbs. Why shouldn't they be? Methods are ways to encapsulate code that does stuff. But as we work on projects for longer and longer, we get lazier and lazier and our code goes from "
register_user_and_email_password()" to "
Obviously, the former is cleaner than the latter, but if we're striving for quality unit testing and we want to make sure that our unit tests are easy to read and in order for them to be easy to read, our function names must be easy to read.
Isn't this easier to read:
$this->assertFalse( register_user_and_email_password( '' ) );
Instead of this?
$this->assertFalse( new_account( '' ) );
Again, perhaps this is a simple example, but the principle remains: Writing good unit tests in order to help self-document the code that drives the language of your functions.
We've talked about the basics of unit testing as well as the key benefits, but we've yet to discuss the disadvantages that come with unit testing and we've not even taken a look at how to incorporate it into our workflow.
So in the next article, we'll attempt to do just that.