This entry is part 4 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
For many plugins, writing adequate unit tests requires the creation of sample post, user, taxonomy, and other data inside of the test WordPress install. While the standard WordPress functions for creating data, such as wp_insert_post(), work just fine, WordPress provides a set helper classes called WP_UnitTest_Factory that make it exceptionally simple.
The WP_UnitTest_Factory class is actually a wrapper for object type specific factory classes that each extend an abstract class called WP_UnitTest_Factory_For_Thing. If you look at the __construct() for WP_UnitTest_Factory, you will see that each object type is set up as a property of the class and holds an instance of the appropriate “thing” class:
These “thing” classes can be used to create, update, and retrieve data for each object type. The following methods are available for each:
- create_object( $args )
- update_object( $args )
- create( $args = array(), $generation_definitions = null )
- create_and_get( $args = array(), $generation_definitions = null )
- create_many( $count, $args = array(), $generation_definitions = null )
With these methods, creating sample data is really simple. Let’s take a look at a few examples.
Creating a Sample Post
To create a simple post, simply call the create() method with a few basic args:
$p = $this->factory->post->create( array( 'post_title' => 'Test Post' ) );
The ID of the post will be returned.
You can now use this to test against. For example, if you want to write a test that checks if searching works, you might have something like this:
The arguments you can pass are identical to those supported by wp_insert_post().
Creating Many Sample Posts
Sometimes you need to write a test that relies on a large amount of post data. The create_many() method is useful for this kind of scenario. WordPress core has a unit test that checks that post counts are working properly after creating draft. The test looks like this:
Create a Sample User
Just like you can create post data, you can also create user data. Let’s look at how WordPress core uses the factory to create a sample user and a password to test whether passwords are properly trimmed:
These factory classes are exceptionally useful and can make testing your code base significantly easier. If you find yourself using the factory a lot, it may be worthwhile to create your own factory classes as well.
By default, WordPress has a factory class for the category and term taxonomies, but no custom taxonomies. If your plugin relies on a custom taxonomy, you may want to consider writing a factory class for it. A simple example would look like this:
You can then use the factory to create Download Categories like so:
This just barely scratches the surface of the helper classes and methods that the WordPress testing suite provides and we will continue to dive into more of them as we continue this series.
Wow! Thanks for this really useful and helpful tutorial about Unit Tests!
First of all, thanks for these tutorials, saved me a lot of pain setting up. I do have one big question though.
I am developing a very database-intensive plugin and I need my own database tables to be present in order to do pretty much anything.
How do you get around to your testing environment to use the custom database tables?
The database will be accessible in exactly the same way as it is in your plugin.
Thanks for the swift response! You are right, I am able to use the same database as my Vagrant install. My remaining issue is, test suite is attempting to use wptests_ database prefix and is not finding my custom tables since they got a wp_ prefix
Take a look at the wp-config-tests.php file. It will let you change the DB prefix just like a standard WP installation.
Some of the methods I am trying to test depend on accessing the global $post. However, using the factory to create a new post doesn’t seem to set $post. How do ensure that $post is available when using the factory?
Correct, it does not. You will want to call setup_postdata() after creating the post record: https://codex.wordpress.org/Function_Reference/setup_postdata
Perfect! That’s exactly what I was looking for. Thanks so much for writing this series!
Happy to help!
This series is great, looking forward to more.
Test test_date_query_before_array() fails when $args is not defined:
$args = array();
$posts = $query->query( $args );
Hey, Pippin!
Thanks for writing this. I think in the second GIST, you added `$args` by mistake. It should be `$query` instead.
public function test_date_query_before_array() {
$p = $this->factory->post->create( array( 'post_title' => 'Test Post' ) );
$query = new WP_Query( array(
's' => 'Test Post'
) );
$posts = $query->query( $query );
$this->assertEqualSets( array( $p ), wp_list_pluck( $posts, 'ID' ) );
}
Hey hey! I know it’s been a bit since this series was started, but will there be more posts on testing? Just checking:-). Thanks for putting this together. Happy to be a Code Monkey here!
This series is likely concluded but that’s not to say I won’t add another part at some point 🙂
Hello,
Thanks for the tuto. I have an issue when writting my own tests. I’m trying to run a test case which requires another plugin to be active. Is there a way to make sure the WP environnement is loaded with the plugins installed?
I’m aware that this is an integration testing but is there a way to test if my plugin is for example extracting properly the affiliates list in AffiliateWP?
You can install the plugin during the bootstrap process in bootstrap.php
I tried that but i must be doing something wrong. For example, in bootstrap.php, i’ve added a require instruction to /AffiliateWP/affiliate-wp.php and yet this instructions does not work:
$affiliates_table = new AffWP_Affiliates_Table();
I checked the affiliate wp source code and it turns out that this class is only included when the is_admin equals true. Then i made sure that the condition is verified and it still does not work. Can you provide a bootstrap.php for an external plugin which run a test with affiliate WP by calling the affiliate_data method from that class? That way i will know what i’m doing wrong.
Thank you so much for taking the time.
Does it give an error when running the tests? If so, what is the error?
No error. I just get a NULL when i var_dump the variable $affiliates_table
This tutorial was an apocalypses to me ! 🙂 I was strangled to find the way to perform WordPress Unit testing for my themes and definitely you gave me the best lesson. Thank you very much.
Thanks for this awesome post. You save my day. Thanks
An intriguing discussion is worth comment. I do think that you
should write more on this issue, it might not be
a taboo matter but typically people do not discuss these
subjects. To the next! All the best!!