Introduction to Unit Testing in WordPress – The Basics

One of the things I like the most about WordPress is its plugin system–there's a lot you can do! For instance, you can download and install our new plugin, Nelio Content.

There’s nothing better than ending your day with a clean conscience knowing you did your job right. Unfortunately, this comforting sensation is quite uncommon among web developers—there are so many things that can go wrong (browsers, servers, libraries…) that it’s complicated to just relax completely, isn’t it?

Bebé cayéndose dormido
“My code’s working! Hurrah! 😬 Now it’s a good time to work on… 😴” (source)

Well then, today’s your lucky day! I’ve prepared a series of posts in which I’ll teach you all you need to know to make sure that (a) your code works and (b) it’ll keep working “no matter what”. As you’ve probably guessed from the title, we’ll be using automated tests. But since there’s plenty of things we can talk about, we’ll be discussing unit testing in WordPress only, both on the server side (PHP) and on the client side (JavaScript). If you want me to cover other topics or kinds of test, just let me know and we’ll squeeze them in 🤔👍.

In this first post we’ll start with just a few definitions about testing and its usefulness. My goal is to give you a clear view of how testing can help you in your daily work, so that (hopefully) you become eager to use it 😇.

The Importance of (Automated) Testing

If you care about your reputation, you should care about the quality of your work too—there’s nothing less professional than uploading a new version of your plugin or theme to the repo and let your users be the ones discovering it simply doesn’t work… and I know what I’m talking about, because it happened to me. Luckily, I learned from my mistakes and implemented several safe guards. It’s impossible to guarantee a bug-free release, but you can minimize the chances by using automated tests.

The usual development cycle of any new release in your plugin or theme looks like this:

  1. You have some new requirements or functionalities that you want to include in your plugin.
  2. You write the code that implements those functionalities.
  3. You test it.
  4. If it fails, you try to fix it and go back to step 3.
  5. Once it’s working, you iterate over steps 2, 3, and 4, until all requirements are met.
  6. You make sure everything’s still working as expected.
  7. You update the new release to WordPress.org.
  8. And… Bam! Someone finds a new bug.

Pretty complicated, huh? There’s a lot of “manual” testing just to make sure things work as they’re supposed to. The problem with “manual” testing is that you test things only once—after verifying they work, you simply forget about them. And that would probably work, if code were static… but code changes, evolves. So, there’s a big chance of breaking stuff as time goes by—when you modify your plugin or theme to include a new functionality, some unforeseen side effects occur and things that used to work no longer do.

Ideally, we need a safe net that guarantees that, if something used to work and it’s no broken, we’ll know before releasing a new version. And that’s precisely what tests are for!

Quien dice una
Well, not only safe nets can do the trick; parachutes work pretty well too 😉 (source)

Depending on the “size” of the tested element, we’ll talk about unit tests (tiny modules), integration tests (how components work altogether) and others, such as acceptance tests (where we put the whole system under test, as if we were a regular user). Regardless of the specific type of test we’re creating, they all follow the same pattern:

  1. Setup. First, you need to prepare the environment, so that the initial state is always the same. For instance, you might want to load a database dump in which there’s only one specific post with a certain ID and title.
  2. Act. Then you apply some actions that will change this initial state (you add a post, change the title, delete a comment).
  3. Check. Finally, you checked that the final state/result is exactly what you expected. Thus, for example, if your blog had one single post in (1) and you removed it in (2), you expect your blog to have no posts at (3).

Unit Tests

Unit Tests are the first step you need to take to guarantee your code works. They check that an atomic piece of your software—a function, usually—works as intended. As any other test, a unit test will simply check that, given a certain input, we get the expected output.

So far it’s all abstract theory… so let’s see an example, shall we? Imagine someone asks you to create a function that, given an amount of money, it returns the corresponding VAT. In Spain, this is set to 21%, so the VAT of something that costs 10€ is 2,10€. This function is super easy to implement, isn’t it?

function nelio_get_vat( $quantity ) {
  return $quantity * 0.21;
}//end nelio_get_vat()

Now, if we call the function as follows: nelio_get_vat(10), we get the expected result: 2.1.

Now, let’s make things a little bit spicier. Assume that $quantity is a value given by the user. Since we’re good programmers, we want to accept multiple formats (instead of forcing the user to input values with a single, specific format):

  • Plain price: 1000
  • Price with thousands separator: 1,000 (comma) or 1 000 (space)
  • Price with currency: 15€15 €15 euros1 euro or 3 EUR
  • Price with and without cents: 15.00, 15.50, or 15.
  • Combining the previous formats: 1,500.00 EUR o 1200.50.
Porque cualquier ejemplo que se precie habla de dinero.
Talking about money feels good, doesn’t it? Well, not as good as owning it, but still…

If we want to accept all those formats, the function can no longer take the input, assume it’s a number and multiply it by 0.21, because there are several scenarios in which $quantity is not a number, but a string with “weird” characters. Regardless of the concrete implementation we end up using, we know that the following statements must be true:

  • If the given price is 1000, the VAT is 210.
  • If the given price is 15 €, the VAT is 3.15.
  • If the given price is 1.200,50 EUR, the VAT is 252.11.

and that’s precisely what a unit test will verify. A unit test checks that, given a certain input, the obtained results is equal to the expected result.  In plain English, a unit test would look like this:

  • Hey nelio_get_vat, if I give you the number 1000, what do I get? I’m hoping for  210.
  • Oye nelio_get_vat, if I give you the string 15 €, what do I get? I’m hoping for 3.15.
  • Oye nelio_get_vat, if I give you the string 1.200,50 EUR, what do I get? I’m hoping for 252.11.

If any of the previous checks (which, by the way, are called assertions) is false, the test will fail and we’ll know something’s wrong with our function. Moreover, if we change the function in the future, our test suite will detect if we broke something unexpectedly—if we did, the test that used to pass will then fail, and we’ll know something’s wrong.

Integration Tests

As we’ve just seen, we use unit tests when we want to test atomic portions of our code (namely, functions; sometimes even classes). But a plugin is much more than individual functions—when all the different pieces of our plugin come together, they serve a greater purpose and solve a bigger problem. If we want to make sure that all the different components work properly altogether, then we need a new type of tests: integration tests.

I could try to describe integration tests, but the following tweet summarizes it perfectly:

Funny, right? This video teaches us the importance of each test has in the final quality of our product. First, it shows that a unit test for both the latch and the door would pass—on the one hand, we can properly slide the latch so that its locked or unlocked and, on the other hand, we can also open and close the door using the handlebar. Great!

The integration test demonstrates that something’s wrong with the system. An integration test would check, for instance, that a closed door with a locked latch can’t be opened. But that’s not what’s happening here! We may have locked the latch, but the door can still be opened. So what’s going on?

Sometimes, we may create components that satisfy their specifications and, therefore, pass their tests, but are not meant to work together as part of a bigger whole. In this case, the type of door we’re using is not compatible with the latch, nor the latch with the door, which means we have to change one or the other so that we fulfill the specifications of a “locked door”.

Acceptance Tests

The last type of tests I’d like to talk about today are acceptance tests, which I discussed thoroughly in my presentation at WordCamp Europe 2015 (in Seville). Acceptance tests are quite similar to integration tests, because they both test the system as a whole.

Integration tests treat the system as a white box. They check the different parts of the plugin/theme being full aware of what’s under the hood. That is, an integration test knows and can check what’s happening in the database, which results are being computed, and so on. It’s equivalent to the kind of test a “developer” would do.

Acceptance tests, on the other hand, validate the system’s behavior assuming it’s a black box. In a black box, you don’t know what your code is doing—you can only see the final result as a regular user would.

Using the appropriate tool (I’ve used Codeception in the past and I love it), you can write tests that simulate the interaction of a user with their browser. For example, if we wanted to check the “Login” process of a WordPress installation, we’d write the following test:

  1. Go to http://local.wordpress.dev/wp-login.php
  2. Use the username “david” and the password “password”.
  3. Click on Log In.
  4. I’m hoping for reading the text “Howdy, David” in the upper right corner of the page.

See the difference? We don’t know how the user is being authenticated, we don’t know anything about what’s under the hood… we’re simply describing an interaction between a user and our system. Cool!

Test Driven Development (TDD)

Before we’re over with this post, I wanted to talk to you about a “new” approach for writing software—Test Driven Development (TDD). If you take a closer look to what I’ve described so far, you’ll see that the workflow is as follows:

  • you write some code,
  • you create the tests that verify it,
  • you execute the tests and make sure that everything works as expected.

Well, TDD is about doing things the other way around:

  • first, you write the test for a new functionality,
  • you verify the test fails (and it will fail, because you haven’t write any code for that new functionality),
  • you write some new code to solve the problem at hand,
  • you execute the test and make sure that, after your new code is over, it passes.

I’ve never used this methodology in my projects, but I do think it’s pretty amazing. So far, I’ve only applied it in tiny examples and some snippets I’ve written for fun, but if we learnt to use it in our daily work, I’m quite sure we’d create better products! 😉

In summary

There are different types of tests, depending on what they test and how they test it. Regardless of the concrete test you’re writing, though, it’ll always follow the same pattern: you prepare the environment, define the actions you want to take, and specify which results you are expecting. If the obtained results match your expectations, things are great. If they don’t, something’s wrong and you need to work on it.

In the next post we’ll get our hands dirty and start to apply some of the concepts you’ve learned today. In particular, I’ll teach you how to unit test the server-code of your plugin/theme using PHPUnit. Stay tuned!

Featured Image by Jack Lyons.

by

He obtained his PhD in Computer Science at UPC. David leads the analysis and design of our services and the user support area. He's interested in a variety of areas, including conceptual modeling, virtual reality, and 3D digital printing. He contributes to the WordPress community by participating in meetups, seminars, and the WCEU.

Leave a Reply

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