This entry is part 3 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
Now that we have gotten through setting up our unit tests and we know the difference between tests and assertions, it is time to begin writing our actual unit tests for our plugin. As mentioned in the previous parts of this series, we are going to be focusing on writing tests for my Restrict Content Pro plugin. I prefer to always try and use real plugins and real world applications for examples as it helps provide a more concrete example of how you can apply the tutorial to your own plugins.
The first thing we are going to do is rename the sample test class that WP CLI created for us in part 2. That sample file is named tests/test-sample.php currently looks like this:
class SampleTest extends WP_UnitTestCase { function testSample() { // replace this with some actual testing code $this->assertTrue( true ); } function test_sample_string() { $string = 'Unit tests are sweet'; $this->assertEquals( 'Unit tests are sweet', $string ); $this->assertNotEquals( 'Unit tests suck', $string ); } function test_sample_number() { $string = 'Unit tests are sweet'; $this->assertEquals( 'Unit tests are sweet', $string ); $this->assertNotEquals( 'Unit tests suck', $string ); } }
Let us start by first renaming the file to something more relevant. When I am creating tests for my plugins, I like to separate the tests into multiple files so that they are logically organized around specific parts of the plugin, just like we organize our classes and functions in our plugins. Each file will be considered a “group” of tests.
Restrict Content Pro deals with user accounts a lot, so I will create a group of tests specific to members. RCP also has discount codes and payment tracking, so I will create test groups for each of those as well.
The files I am starting with are:
- tests/test-members.php
- tests/test-discounts.php
- tests/test-payments.php
Each of these files contains an extension of the WP_UnitTestCase class, like this:
class RCP_Member_Tests extends WP_UnitTestCase { function testSample() { // replace this with some actual testing code $this->assertTrue( true ); } }
The name of the class does not really matter, just name it something that makes sense.
Now when we run our tests, we will see this:
Let’s go ahead and write a few tests for each of our groups.
For our members group, we will write some tests that check whether our status and expiration date functions work properly.
Restrict Content Pro has a function called rcp_get_status(). This function retrieves the status of the specified user ID. The status can be:
- free
- active
- pending
- cancelled
- expired
Each of the statuses refers to the member’s account status. By default, all members are given the free status, so the rcp_get_status() function is supposed to return free if the specified member does not have a status assigned to their account. Let’s write a test to check if that happens:
function test_get_default_status() { $status = rcp_get_status( 1 ); $this->assertEquals( 'free', $status ); }
This simply retrieves the status of the user account created when PHPUnit runs and checks their status. If the status returned is free, the test passes, which it does:
Okay, let’s write another test now. Restrict Content Pro has another function for setting the status of member accounts called rcp_set_status(). This function lets you update a member’s account to have a specific status. To test that this function is working properly, we are going make multiple assertions. We will set the status, then call rcp_get_status() again to retrieve the status and compare it to what we passed to rcp_set_status(). If the statuses match, our assertion passes.
function test_set_status() { rcp_set_status( 1, 'active' ); $status = rcp_get_status( 1 ); $this->assertEquals( 'active', $status ); rcp_set_status( 1, 'pending' ); $status = rcp_get_status( 1 ); $this->assertEquals( 'pending', $status ); rcp_set_status( 1, 'cancelled' ); $status = rcp_get_status( 1 ); $this->assertEquals( 'cancelled', $status ); rcp_set_status( 1, 'expired' ); $status = rcp_get_status( 1 ); $this->assertEquals( 'expired', $status ); }
We can now run phpunit again to see if our tests still pass, which they do:
Now let’s move on and write a couple of tests related to discount codes.
Restrict Content Pro displays a field on the registration form for the customer to enter a discount code, but the field is only displayed if there is at least one active discount available in the database. To determine if there is at least one discount code, Restrict Content Pro has a function called rcp_has_discounts(). Let’s write a quick test for this.
When first installed, the Discounts table is empty, so rcp_has_discounts() should return false. Our test will prove that is the case:
function test_has_discounts() { $this->assertFalse( rcp_has_discounts() ); }
Does out test pass? Yes it does:
Alright, let’s write one more discount code related test now. We will write one to test that inserting a new discount code works.
To create new discount codes, Restrict Content Pro has a class called RCP_Discounts with an insert() method. We will use this in our test:
function test_insert_discount() { $discounts_db = new RCP_Discounts; $args = array( 'name' => 'Test Code', 'code' => 'test', 'status' => 'active', 'amount' => '10' ); $discount_id = $discounts_db->insert( $args ); $this->assertGreaterThan( 0, $discount_id ); }
This test instantiates the RCP_Discounts class and then calls the insert() method to store the discount. The insert() method returns the ID of the discount created if successful and false if it fails. We can check if it was successful by checking that the return value is greater than 0, which it is:
Restrict Content Pro has many, many, many more functions and class methods that need to have tests written for them, but these will serve as good examples to help get you started with writing tests for your own plugin.
There are still many more areas to cover with unit testing. In the next part of this series, we will look at some of the tools the WordPress unit testing suite provides us with to help making testing easier. One example of this is the “factory”, a tool for creating data to test against.
Thanks very much for the series so far – has helped ease me into TDD immeasurably!
Think you might have a small typo here btw: “We can not run phpunit again to see if our tests still pass, which they do” (think that ‘not’ might be a bit misplaced)
Thanks again and looking forward to the rest of the series
John
Glad to hear it!
Thanks for noticing the typo, I’ve corrected it.
Thanks for the series! Do you have any idea if the next part is on its way anytime soon?
Also, it seems as if these tests are run with “persistent” data by default–the data you add to the database in one test is there in the other tests. Is there any mechanism in PHPUnit/WP for doing each test with a clean database?
I’m hoping to get it finished within a week or two.
I’m pretty sure there is a way to adjust the persistance and will try to cover that in a future part.
Are you still working on the next entry in the series?
Yep! I’m hoping to get it finished up next week.
It’s live! https://pippinsplugins.com/unit-tests-for-wordpress-plugins-the-factory/
You’re a great teacher!
I love the way you explain things.
I love your website design which is absolutely free of distractive elements (not like tons of other websites which try to teach something but they leave millions of annoying elements on the page e.g. ads, bad typography etc).
The typography of the website is so cool.
Thanks for sharing your knowledge with us.