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:
with a list of all the posts that match the search criteria:
or a warning message when no posts were found:
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
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!
functions.js in the root directory of our plugin.
And complete this first step by activating the plugin in your Dashboard 😄
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
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
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
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!
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…
BOOM! Flying colors! We nailed it 😎 High five! ✋
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?
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.