This entry is part 1 of 4 in the Unit Tests for WordPress Plugins Series
- Unit Tests for WordPress Plugins – An Introduction
- Unit Tests for WordPress Plugins – Setting Up Our Testing Suite
- Unit Tests for WordPress Plugins – Writing Tests
- Unit Tests for WordPress Plugins – The Factory
Unit tests are a tool that developers use to test that code is performing properly. The idea behind them is to create a set of tests to determine if your code, given a known data set, gives the expected response.
A unit test should check that one piece of code, such as a function or method, runs properly and returns the expected results.
Through this tutorial series, we will walk through how to setup unit tests for your plugin, how to write the unit tests, how to run the tests, how to write testable code, and also some of the tools that we have available to us to help us leverage the benefits of unit tests. I also want to try and answer the question of “why” you should be writing unit tests for your plugin.
As we go through this tutorial series, I will be writing real unit tests for real plugins to try and better illustrate exactly how they work and how you can implement them in your own plugins.
Let’s look quickly at a simple example of how unit tests are valuable and what we use them for. This is just a simple example and there are many reasons beyond this, but let’s start simple.
Imagine we have a function in our plugin called get_tax( $price ), and this function gets the amount of tax charged for a certain price. If our tax rate is 10% and our price is $10, then we know with some simple math (and assuming simple tax laws) that the amount of tax amount should be $1. The question at this point is whether or not our function, get_tax( 10 ), returns 1 or does it return something unexpected? By writing some simple unit tests, we can determine whether our get_tax() function is working properly.
A good unit test for our get_tax() function would try passing in various numbers in various formats to determine if the results are always what we expect them to be.
For example, our function should be able to account for all of the following number formats:
- 10
- $10
- 10.00
- $10.00
- $10.25
- 1,000
- $1000
- $1,000
- $1,023.522234
- 10.087622244
If our function is working properly, it will be able to accurately calculate tax on all of those numbers, even with their inconsistent formatting. Note, this is assuming that our get_tax() function includes some kind of input sanitization. We will assume it does.
By writing unit tests that check how the function performs with this variety of inputs, we can better know how reliable our function is.
In English, unit tests for this function would look something like this:
Hey function, if I give you $10, what do I get? I’m hoping for 1.
Hey function, if I give you $1,000, what do I get? I’m hoping for 100.
Hey function, if I give you 10.00233113552, what do I get? I’m hoping for 1.
If any of these checks (we call them assertions) fail, then we know that our function is not functioning properly (assuming our test isn’t broken).
A unit test is nothing more than a programmatic check to determine if a chunk of code performs the way it is supposed to.
So as to not leave you without seeing a real unit test, here’s a real test that we use in Easy Digital Downloads:
public function test_get_sales_by_date() { $stats = new EDD_Payment_Stats; $sales = $stats->get_sales( 0, 'this_month' ); $this->assertEquals( 1, $sales ); } |
This unit test is testing whether the get_sales() method of our EDD_Payment_Stats class is functioning properly. We know (because we created it shortly before running this test) that the number of sales for the current month is 1, so does our get_sales() method work properly and return one?
In English, $this->assertEquals( 1, $sales ); says:
Hey get_sales(), if I get sales for the current month, do I get 1 like I hope I do?
In the next part of this series, we will utilize WP-CLI to setup a basic unit tests suite for a simple plugin, then we’ll dive right into writing unit tests.
Excited about this series!
EDD’s testing framework has been my go to source of inspiration for a few months now: https://github.com/easydigitaldownloads/Easy-Digital-Downloads/tree/master/tests
Cheers,
Eric
Glad to hear it!
Super excited about this series. Unit testing has been on my list of things to do for awhile now.
The dog’s bollocks, nice intro!
I’m very excited about this series. Waiting for the next post!
Thanks for kicking this off. Looking forward to it.
If you didn’t see it, part 2 is now available! https://pippinsplugins.com/unit-tests-wordpress-plugins-setting-up-testing-suite/
Thank you for sharing this, Pippin. You make this easier to be understood 😀
Pippin,
Isn’t this introducing an external dependency of the EDD_Payment_Stats class and the EDD_Payment_Stats::get_sales() method?
Shouldn’t you stub the result instead of actually get the result from the class/method?
-Mat
RE:
“`
$stats = new EDD_Payment_Stats;
$sales = $stats->get_sales( 0, ‘this_month’ );
“`
shouldn’t you mock EDD_Payment_Stats? You are creating a dependency outside of the SUT
I don’t really see it being any different than calling a global function. Maybe I’m missing something. Could you elaborate?
Derp. Completely ignore what I said above. For some reason I thought you were adding a call to a method of a different class (outside of the class you were testing) within that test. Sorry about that.
Also as an aside of sorts, I know this is intended as an example, but isn’t the `get_sales()` method a moving target? You don’t know how many sales there would be. You could just assert that it’s an integer e.g. `$this->assertTrue( is_int($sales) );`
Also, if you run a coverage report you won’t have 100% coverage of the method. Briefly glancing at the method (as of this post https://github.com/easydigitaldownloads/Easy-Digital-Downloads/blob/master/includes/payments/class-payment-stats.php#L36) you are testing there’s a bunch of conditionals. To cover all of these I would either use a data provider (https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.dataProvider) to make sure that you are hitting every single case, or if you find that you are hacking a bit with the data provider, create separate tests that test each conditional and provide the expected output. As you’ve been doing, continue to test yourself with a coverage report (https://phpunit.de/manual/current/en/code-coverage-analysis.html). Use the @covers annotation (https://phpunit.de/manual/3.7/en/appendixes.annotations.html#appendixes.annotations.covers) to keep yourself honest as well.
You may already be doing this because I couldn’t find exactly where you are testing the method since I didn’t see a test_payment_stats or similar file…
Great work though, I’m pretty green when it comes to writing unit tests and it makes me happy that others are doing great things with them.
We know how many sales we should have because we created the sales during the setUp() method at the top of our unit tests. Sorry, this wasn’t shown in the example but you can see it in the EDD unit tests: https://github.com/easydigitaldownloads/Easy-Digital-Downloads/blob/master/tests/tests-stats.php#L15
great stuff!
This is a good introduction to unit testing with PHP. I just wanted to point out that your code snippet is not displaying properly in this post. It’s displaying -> instead of the ->. I actually didn’t realize what was going on at first and spent a couple of minutes trying to decipher it!
I had the same issue recently while working on a blog article, and I think you can convert it to html entities and then it will display correctly. Or, maybe do whatever you did in later posts in this series, as all of the code snippets render correctly in them.
Fixed 🙂
This question isn’t about testing, is about what you use for the series, after title, you have a link for next post in serie, and a link for previous post in the serie.
You use a plugin? A short code? Simple html?
Thank you for some other wonderful article. The place else may anybody get that type of info in such an ideal method of writing?
I’ve a presentation subsequent week, and I am at the search for such information.
Everything posted was very logical. But, consider this, what if you were to create a
killer headline? I am not saying your content is not good,
but what if you added something that makes people want more?
I mean Unit Tests for WordPress Plugins – Introduction is
kinda boring. You should peek at Yahoo’s home page and watch how they create article titles to grab viewers interested.
You might try adding a video or a picture or two
to grab readers excited about what you’ve got to say. Just my
opinion, it would bring your blog a little livelier.
It’s hard to come by experienced people for this topic, but you sound like you know what you’re talking about!
Thanks
I am genuinely thankful to the owner of this web site who has shared this impressive post
at here.
If you desire to increase your experience only keep visiting this web site and be updated with the newest news update
posted here.