Unicorn, by Yosuke Muroya

Finally! Today we share the last post in our unit testing series. If you’ve read (and enjoyed) the previous posts, congratulations! You now know what unit tests are and how to use them, how to test your PHP code with PHPUnit and how to test your JavaScript code with QUnit. We learned quite a lot with only three posts, didn’t we? But I think we still need to take one step further and test our new skills in the real world with a “real” example.

As I already mentioned in the previous post, today I’ll teach you how to test the different components of a “real” plugin. In particular, I’ll show you how to test the PHP and JavaScript components of a plugin that uses AJAX intensively.

The Plugin – A Post Searcher

Today, I want you to test your new testing skills with a real example. So, the first thing we need to do is the plugin we’ll be testing–simple enough to implement, complex enough to create interesting tests.

The plugin we’ll create is a Post Searcher. It consists of a simple form that we’ll include at the end of all our posts. When the user types some string in the form, our plugin will perform an AJAX request and will recover all the posts whose title contain the query string. Something like this:

Post Search Form
Screenshot of our post search form. A simple text input waiting for the user to type in something!

with a list of all the posts that match the search criteria:

Result List in our Post Search Plugin
List of posts that match the search criterion.

or a warning message when no posts were found:

No results warning
If we don’t find anything, we should tell the user.

Quite easy, isn’t it? Time to code!

1. The Skeleton

As you may already know, you’re free to organize the plugin‘s code as you want. Personally, I like to start my (big) projects with a solid skeleton, such as the one provided by the WordPress Plugin Boilerplate.  However, today I want to keep things simple, so, if you don’t mind, I’ll be adding all the functions in a single PHP file–it’s not the most elegant solution, but it works.

Let’s create our plugin‘s main file. First, create a directory named nelio-post-searcher inside wp-content/plugins/ and add a file named nelio-post-searcher.php with the following content:

And we’re done! No, I’m kidding–you just created a plugin that does nothing. Your first void plugin. Hurrah!

Our plugin will implement an AJAX call, so let’s prepare the files that’ll contain our JavaScript code. Create two empty files: searcher.js and functions.js in the root directory of our plugin.

And complete this first step by activating the plugin in your Dashboard ?

2. Add the Search Form and Enqueue JavaScript Files

Let’s add the form at the end of our posts. If you’ve ever worked with WordPress hooks, it’s actually quite easy: just append the form using the filter named the_content (read its documentation). Add the following snippet into your PHP file:

As you can see, we’re appending a text input and a result div at the end of the post. Again, this is not the best solution (we’re mixing “logic” with “presentation”), but it works and it’s easy. If you want to implement it better, you might want to create a separate file in a partials directory and include it in the function… but it’s up to you, dear friend!

Remember we said this search form will use AJAX calls, so we need to add our scripts too. Just enqueue the scripts as follows:

and our brand-new, empty scripts will be appended to your front-end.

3. Implement the Search Function…

Now we need to implement the search function. We’re interested in retrieving “all” the posts whose title contain a certain query string, which means the following function will be perfect:

because it takes the search query as an input parameter and returns an array with the results. If you inspect the function closely, you’ll realize that WP_Query uses an argument named post_title__like, which I’m afraid does not exist in WordPress by default. Let’s create it with an extra function:

You might be wondering why we need this function. Well, by default WordPress‘ search function returns all the posts that contain the query string in their title or their content. Since we’re only interested in finding posts with a match on their titles, we need this extra parameter.

4. …and Create the AJAX Callback that Uses This Function

The function above is quite beautiful. Maybe it even works properly… But it serves no purpose if we don’t use it somewhere! So, let’s expose it using an AJAX callback so that we can use from JavaScript. Just add the following snippet into your file:

This new function simply checks that there’s a query parameter (named q) in the AJAX request. If it doesn’t exist, we return an error. If it does, we use our search function and return the results. It’s important to notice that we sanitize the input before we use it–this is something you always need to do before using any input provided by the user.

It’s also worth noticing that this function doesn’t have any return statements–it’s an AJAX callback and, as such, its execution ends when we use wp_send_json*.

5. Let’s Write Some JavaScript!

Our server-side code is ready! Let’s write some JavaScript to use it, shall we? If you saw Rebecca Murphey’s video, you already know how to write testable JavaScript code. If you haven’t seen it yet, stop reading this post, go see it right away, and we’ll continue when you’re back.

The code that we need to write now defines the interaction between the user and the search form. Essentially, this is what we want:

  • When the user types something in the search field, we should tell them that “we’re searching” their posts.
  • When they’re done, we need to perform the AJAX request and recover the matching results.
  • Once we get the results, we draw them on screen.
  • If there aren’t any, we tell the user we didn’t find anything.
  • If the user clears the search input, we also clean the result area.

If you want to implement a different behavior or a different form, please feel free to do it. This post is just a set of guidelines presented in an example that you can follow ? Just like PHP, you can write and organize your code as you want. But if you decide to apply the ideas Rebecca shared in her video, you might end up with a searcher.js file similar to this:

I know it’s a lot of code, but it’s easier that it seems. First, we have a function named onQueryChanged that listens to changes in the input field and, after the user stops typing (we simulate this using underscorejs.org‘s _.debounce function), triggers an AJAX request to load the results using a wrapper function named searchPosts. Once the AJAX request succeeds, we process the results using a function named processResult, which checks if the request was successful or not. If it was, we use the draw function to show the results. Finally, if the user cleans the text input, we clear the result area too.

If you refresh a post now and check if your code works, you’ll see it doesn’t. That’s because we simply created the search functions, but we haven’t set them in motion yet. To do that, we need to create some “glue code” in your functions.js:

Testing Our Plugin

Finally, time to test our code! If you’re like I was some time ago, you’ll probably test the code manually. That is, you’ll open your browser and check if things work as expected by typing some data and looking at the results. And, hey, you should probably keep doing this regardless of your testing approach… but now you also know that you must create some unit tests!

The biggest mistake we’ve done during the past few minutes is the fact that that we’ve written a lot of code without testing anything at all. Sure, I know we’re very good at it, but writing some tests as we add new code will guarantee that (a) the tests exist and (b) that they test the important things.

Well, don’t worry, we still have time! Let’s see several examples of the test we could have written.

Testing PHP with PHPUnit

Since we started with our PHP code, testing PHP first makes sense, doesn’t it? Let’s start with something simple–let’s make sure that the form we added at the end of our posts is actually there:

The test above creates a post in our testing environment that we can then use. Once the post is created, we retrieve it and look at its content (after we applied the the_content filter). If our form has been successfully added, the content should include the IDs of our text input and result div–and that’s exactly what we assert. Easy peasy!

I’d also like to check that our JavaScript scripts have been successfully included in the front-end. But, hey, you take care of this one! ?

Next we need to make sure that the most important function in our plugin–the post search function–works as expected. It’s up to you to decide what this “expected behavior is”, and depending on your decisions you need to test one thing or the other. For example, how should the function respond in the following scenarios?

  • What if the query string is empty?
  • What if there aren’t any posts in WordPress?
  • If there aren’t any posts that match the criteria, what does the function return?
  • What if multiple posts match the criteria?

As I said, you‘re the one who decides what’s right and wrong here. I thought about all these scenarios and I came up with the following expected behaviors (which I already coded in a test):

Finally, we need to make sure that the AJAX call works as expected. Since we already tested the search function thoroughly in the previous test, there isn’t much to test in this function—we simply need to make sure that the callback deals with the input parameters properly. For instance, we said we wanted the function to return an error if no query is provided, and a list of posts if it is:

There are two interesting things to notice in this test. First, the test inherits from a class named WP_Ajax_UnitTestCase. This class let’s us simulate AJAX requests, as you can see in the try-catch block. Second, we haven’t included any tests to verify that the returned list of posts is correct—we already tested this when we tested the neliops_search_posts function, so why should we repeat our work?

Once we’re done, we launch PHPUnit and…

Testing AJAX in PHP
Testing AJAX in PHP and… great! It all works!!

BOOM! Flying colors! We nailed it ? High five! ✋

Testing JavaScript with QUnit

The last thing we need to test is, as you can imagine, our JavaScript code. The most important thing to keep in mind is that it’s not your responsibility to check that your AJAX endpoint works as expected—you must assume that all your server calls work as expected (in this case, we already tested them). In fact, you shouldn’t even use the real callbacks anyway—you must fake the data and test your code with it.

Let’s take a look at the easiest example! Does our code draw the list of posts properly? Well, it surely does if the following tests pass:

See how easy it is? Since our code uses several isolated functions, testing them individually is straightforward—each function has a clear responsibility, and so are their tests! Now imagine what would have happened if you used anonymous functions and wrote spaghetti code.

What about the other tests? Do we process the AJAX results properly? Is the result area properly cleaned when there isn’t any text in the form? There’s a lot of tests you can write! You dare?

Conclusions

And that’s all I wanted to share, folks! Thanks for reading all my posts ? Today we’ve tested our newly acquired skills. We learned how to use unit tests to test our components individually and ensure that they behave as expected. If we were to test how they work altogether, we’d have to create integration tests (which are out of the scope of this series).

Now, one final question: are you able to write the tests first (with no code at all), run them (and see them fail), and then write the code so that the tests pass? If you are, congratulations! You’re now a Test Driven Developer. I strongly suggest you give it a try and share your experience with us.

Happy new year!

Featured Image by Yosuke Muroya.

Leave a Reply

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

I have read and agree to the Nelio Software Privacy Policy

Your personal data will be located on SiteGround and will be treated by Nelio Software with the sole purpose of publishing this comment here. The legitimation is carried out through your express consent. Contact us to access, rectify, limit, or delete your data.