In “WordPress PHPUnit Tests” definitive guide, I am going to explains all about the PHP unit testing in WordPress.
I try to cover all the possible topics in this guide including setting up PHP unit test environment, writing tests, etc.
In this guide you are going to learn:
Create a sample plugin Create a sample plugin
Instead of of existing plugin, Lets create a new plugin from a scratch and implement the PHP unit test cases within it.
Step 1: Create a empty directory sample-plugin
Step 2: Create a new file sample-plugin.php
Step 3: Copy and paste below below code within it.
<?php /** * Plugin name: Sample Plugin */
Step 4: That’s it. We have created a new plugin. See it into the plugins list screen as below:

Install the PHPUnit files and folders Install the PHPUnit files and folders
WordPress provide the PHPUnit test files for us. So, We just need to use it.
We need to use the command wp scaffold plugin-tests
to setup the WordPress PHPUnit test cases for our plugin.
Follow below steps:
Step 1: Open the terminal or command prompt (CMD)
Step 2: Navigate to \wp-content\plugins\sample-plugin
directory.
Step 3: Execute command wp scaffold plugin-tests sample-plugin
E.g.
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ wp scaffold plugin-tests sample-plugin Success: Created test files.
Step 4: Now, you can see the PHP Unit files
.phpcs.xml.dist .travis.yml phpunit.xml.dist ├───bin ├──────install-wp-tests.sh ├───tests └──────bootstrap.php └──────test-sample.php
Here, We have a all the file structure to execute the unit tests.
Setup Test WordPRess Setup Test WordPRess
Note: If you already have a WordPress test setup for executing the PHPUnit tests then you can skip this section.
The test WordPress setup is installed into the temp directory. To setup the test WordPress instance.
No worry. With the scaffold command we have a install-wp-tests.sh
file within the bin
directory.
├───bin ├──────install-wp-tests.sh
The install-wp-tests.sh
create us the test WordPress setup.
So, lets execute it.
bash bin/install-wp-tests.sh
e.g.
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ bash bin/install-wp-tests.sh usage: bin/install-wp-tests.sh <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]
Here, We are getting the error to provide additional information to create a test WordPress setup.
Syntax:
bash bin/install-wp-tests.sh <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]
Here we need the database, username, password, host. Other fields are optional.
Create a test database. I’m using a Xampp so, I’m going to create a database from http://localhost/phpmyadmin/.
Screenshot:

Here, I have created a new database wordpress_test
.
Now, We have a database to create a test WordPress site.
Lets execute the command by passing the database, username, password, host as below.
E.g.
bash bin/install-wp-tests.sh wordpress_test root '' localhost
After executing the command you can see somehting like below:
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ bash bin/install-wp-tests.sh usage: bin/install-wp-tests.sh <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation] C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ bash bin/install-wp-tests.sh wordpress_test root '' localhost + install_wp + '[' -d /tmp/wordpress/ ']' + mkdir -p /tmp/wordpress/ + [[ latest == \n\i\g\h\t\l\y ]] + [[ latest == \t\r\u\n\k ]] + '[' latest == latest ']' + local ARCHIVE_NAME=latest + download https://wordpress.org/latest.tar.gz /tmp/wordpress.tar.gz ++ which curl + '[' /mingw64/bin/curl ']' + curl -s https://wordpress.org/latest.tar.gz C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ bash bin/install-wp-tests.sh wordpress_test root '' localhost + install_wp + '[' -d /tmp/wordpress/ ']' + mkdir -p /tmp/wordpress/ + [[ latest == \n\i\g\h\t\l\y ]] + [[ latest == \t\r\u\n\k ]] + '[' latest == latest ']' + local ARCHIVE_NAME=latest + download https://wordpress.org/latest.tar.gz /tmp/wordpress.tar.gz ++ which curl + '[' /mingw64/bin/curl ']' + curl -s https://wordpress.org/latest.tar.gz C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ bash bin/install-wp-tests.sh wordpress_test root '' localhost + install_wp + '[' -d /tmp/wordpress/ ']' + mkdir -p /tmp/wordpress/ + [[ latest == \n\i\g\h\t\l\y ]] + [[ latest == \t\r\u\n\k ]] + '[' latest == latest ']' + local ARCHIVE_NAME=latest + download https://wordpress.org/latest.tar.gz /tmp/wordpress.tar.gz ++ which curl + '[' /mingw64/bin/curl ']' + curl -s https://wordpress.org/latest.tar.gz + tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C /tmp/wordpress/ + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php /tmp/wordpress//wp-content/db.php ++ which curl + '[' /mingw64/bin/curl ']' + curl -s https://raw.github.com/markoheijnen/wp-mysqli/master/db.php + install_test_suite ++ uname -s + [[ MSYS_NT-6.1-7601 == \D\a\r\w\i\n ]] + local ioption=-i + '[' '!' -d /tmp/wordpress-tests-lib ']' + '[' '!' -f wp-tests-config.php ']' + download https://develop.svn.wordpress.org/tags/5.4.2/wp-tests-config-sample.php /tmp/wordpress-tests-lib/wp-tests-config.php ++ which curl + '[' /mingw64/bin/curl ']' + curl -s https://develop.svn.wordpress.org/tags/5.4.2/wp-tests-config-sample.php ++ echo /tmp/wordpress/ ++ sed 's:/\+$::' + WP_CORE_DIR=/tmp/wordpress + sed -i 's:dirname( __FILE__ ) . '\''/src/'\'':'\''/tmp/wordpress/'\'':' /tmp/wordpress-tests-lib/wp-tests-config.php + sed -i s/youremptytestdbnamehere/wordpress_test/ /tmp/wordpress-tests-lib/wp-tests-config.php + sed -i s/yourusernamehere/root/ /tmp/wordpress-tests-lib/wp-tests-config.php + sed -i s/yourpasswordhere// /tmp/wordpress-tests-lib/wp-tests-config.php + sed -i 's|localhost|localhost|' /tmp/wordpress-tests-lib/wp-tests-config.php + install_db + '[' false = true ']' + PARTS=(${DB_HOST//\:/ }) + local PARTS + local DB_HOSTNAME=localhost + local DB_SOCK_OR_PORT= + local EXTRA= + '[' -z localhost ']' ++ echo ++ grep -e '^[0-9]\{1,\}$' + '[' ']' + '[' -z ']' + '[' -z localhost ']' + EXTRA=' --host=localhost --protocol=tcp' + mysqladmin create wordpress_test --user=root --password= --host=localhost --protocol=tcp
One more thing we need to setup for the Windows user.
You can get something below error while executing the PHP unit tests.
PHP Warning: require_once(/tmp/wordpress//wp-includes/class-phpmailer.php): failed to open stream: No such file or directory in C:\Users\ADMIN\AppData\Local\Temp\wordpress-tests-lib\includes\mock-mailer.php on line 2 PHP Stack trace: PHP 1. {main}() C:\xampp\php\phpunit-7.5.20.phar:0 PHP 2. PHPUnit\TextUI\Command::main() C:\xampp\php\phpunit-7.5.20.phar:620 PHP 3. PHPUnit\TextUI\Command->run() phar://C:/xampp/php/phpunit-7.5.20.phar/phpunit/TextUI/Command.php:162 PHP 4. PHPUnit\TextUI\Command->handleArguments() phar://C:/xampp/php/phpunit-7.5.20.phar/phpunit/TextUI/Command.php:173 PHP 5. PHPUnit\TextUI\Command->handleBootstrap() phar://C:/xampp/php/phpunit-7.5.20.phar/phpunit/TextUI/Command.php:863 PHP 6. PHPUnit\Util\FileLoader::checkAndLoad() phar://C:/xampp/php/phpunit-7.5.20.phar/phpunit/TextUI/Command.php:1058 PHP 7. PHPUnit\Util\FileLoader::load() phar://C:/xampp/php/phpunit-7.5.20.phar/phpunit/Util/FileLoader.php:45 PHP 8. include_once() phar://C:/xampp/php/phpunit-7.5.20.phar/phpunit/Util/FileLoader.php:57 PHP 9. require() C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin\tests\bootstrap.php:34 PHP 10. require_once() C:\Users\ADMIN\AppData\Local\Temp\wordpress-tests-lib\includes\bootstrap.php:87
Here, I’m getting an error because of the constant ABSPATH
absolute path is not set.
This is a known error for the Windows environment.
So, the fix is we need to set the ABSPATH
of our WordPress test setup.
Todo this:
- Open file
C:\Users\{YOURNAME}\AppData\Local\Temp\wordpress-tests-lib\wp-tests-config.php
- Serach for:
define( 'ABSPATH', '/tmp/wordpress/' );
- Replace with
define( 'ABSPATH', 'C:\Users\{YOURNAME}\AppData\Local\Temp/wordpress/' );
Note: Here you need to use your {YOURNAME}
with your current system username. My usernmae is ADMIN
So, I have search for define( 'ABSPATH', '/tmp/wordpress/' );
with define( 'ABSPATH', 'C:\Users\ADMIN\AppData\Local\Temp/wordpress/' );
Now, our test site is ready to use for the PHPUnit tests.
Configure the PHPUnit file Configure the PHPUnit file
Create a duplicate copy of phpunit.xml.dist
and name it phpunit.xml
Keep the content of the file as it is.
Your phpunit.xml
file contain something:
<?xml version="1.0"?> <phpunit bootstrap="tests/bootstrap.php" backupGlobals="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" > <testsuites> <testsuite> <directory prefix="test-" suffix=".php">./tests/</directory> <exclude>./tests/test-sample.php</exclude> </testsuite> </testsuites> </phpunit>
Testing PHPUnit Test Testing PHPUnit Test
Now execute the command phpunit
into the plugin directoory.
E.g..
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. Warning - The configuration file did not pass validation! The following problems have been detected: Line 11: - Element 'testsuite': The attribute 'name' is required but missing. Test results may not be as expected. Time: 13.59 seconds, Memory: 38.00 MB No tests executed!
Screenshot

Here, The testsuite name is recommend for that we are getting warning:
Element 'testsuite': The attribute 'name' is required but missing.
Lets add a testsuite name.
- Open the file
phpunit.xml
- replace the
<testsuite>
with<testsuite name="default">
.
Now, Run the command phpunit
E.g.
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. Time: 17.48 seconds, Memory: 38.00 MB No tests executed!
Screenshot – https://i.imgur.com/IMLNYzA.png
Yup! Our PHPunit works as expected.
Here we are getting the output No tests executed! because of we have not any test case yet.
Dont worry. We have a sample test case file /tests/test-sample.php
.
To make it work open the file phpunit.xml
and remove <exclude>./tests/test-sample.php</exclude>
.
Now, Execute the phpunit
command.
E..g
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 23.2 seconds, Memory: 40.00 MB OK (1 test, 1 assertion)
Screenshot – https://i.imgur.com/Zwwbnbk.png
Here, We gtting the result OK (1 test, 1 assertion)
.
Our first test case from the file wp-content\plugins\sample-plugin\tests\test-sample.php
executed and works as expected.
Lets see what is the test case into the file test-sample.php
See test cases. See test cases.
Open file test-sample.php
you can see something:
<?php /** * Class SampleTest * * @package Sample_Plugin */ /** * Sample test case. */ class SampleTest extends WP_UnitTestCase { /** * A single example test. */ public function test_sample() { // Replace this with some actual testing code. $this->assertTrue( true ); } }
Here,
- The PHP class SampleTest extends with WP_UnitTestCase it means that we can use the methods of WP_UnitTestCase into our testcase class SampleTest. We’ll see these methods in next article.
- We have a method test_sample(). Note that every function need to be prefixed with
test_
. - In the
test_sample()
function we have a code$this->assertTrue( true );
- As per its name the function
assertTrue()
expecte the value should be atrue
. So, When we execute the PHPUnit test then we get the success message.
Now, Lets make a small change to fail our test case.
Add the falue value within the assertTrue().
E.g. Change $this->assertTrue( true );
with $this->assertTrue( false );
Now, Execute the phpunit command.
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 21.95 seconds, Memory: 42.00 MB There was 1 failure: 1) SampleTest::test_sample Failed asserting that false is true. C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin\tests\test-sample.php:18 FAILURES! Tests: 1, Assertions: 1, Failures: 1.
Screenshot https://i.imgur.com/JdjHidU.png
Here, We see that 1 test, 1 assertion, and 1 assertion.
we also see that the function SampleTest::test_sample
failed because of error Failed asserting that false is true.
on line test-sample.php:18
.
Here, We have used a function assertTrue(). Now, lets
Test the dynamic content Test the dynamic content
We can test our functions and provide them some expected inputs. But, Additionally we can test the functionality which is depends on the dynamic content such as posts, pages or custom post types.
We can create the same post, pages and custom post types for our testing.
WordPress provides the PHP base class WP_UnitTest_Factory
which some below helper classes to make it work.
- WP_UnitTest_Factory_For_Post
- WP_UnitTest_Factory_For_Term
- WP_UnitTest_Factory_Callback_After_Create
- WP_UnitTest_Factory_For_Attachment
- WP_UnitTest_Factory_For_Blog
- WP_UnitTest_Factory_For_Bookmark
- WP_UnitTest_Factory_For_Comment
- WP_UnitTest_Factory_For_Network
- WP_UnitTest_Factory_For_Thing
- WP_UnitTest_Factory_For_User
Lets create a simple post and check it has the expected post title and the post type.
The object $this->factory
is the object of WP_UnitTest_Factory
class which contain:
$this->post = new WP_UnitTest_Factory_For_Post( $this ); $this->attachment = new WP_UnitTest_Factory_For_Attachment( $this ); $this->comment = new WP_UnitTest_Factory_For_Comment( $this ); $this->user = new WP_UnitTest_Factory_For_User( $this ); $this->term = new WP_UnitTest_Factory_For_Term( $this ); $this->category = new WP_UnitTest_Factory_For_Term( $this, 'category' ); $this->tag = new WP_UnitTest_Factory_For_Term( $this, 'post_tag' ); $this->bookmark = new WP_UnitTest_Factory_For_Bookmark( $this ); if ( is_multisite() ) { $this->blog = new WP_UnitTest_Factory_For_Blog( $this ); $this->network = new WP_UnitTest_Factory_For_Network( $this ); }
So, The $this->factory->post
object which which is the object of Class WP_UnitTest_Factory_For_Post
and it is extended from WP_UnitTest_Factory_For_Thing
.
We are going to use the $this->factory->post
to create a new post.
Lets check it with example. Copy the below code and paste it into the \wp-content\plugins\sample-plugin\tests\test-sample.php
file.
/** * Create a new post */ public function test_create_new_post() { $post_id = $this->factory->post->create( array( 'post_title' => 'Hello World', 'post_type' => 'post', ) ); // Check the expected post title. $this->assertEquals( 'Hello World', get_the_title( $post_id ) ); // Check the expected post type. $this->assertEquals( 'post', get_post_type( $post_id ) ); }
Lets see what is the above code.
1) We have used $this->factory->post
and call a function create()
and pass the parameters
array( 'post_title' => 'Hello World', 'post_type' => 'post', )
These are the post parameters. Here the new post is created with the title Hello World
2) We have used two assertEquals() functions. The first one for to test the title of the post and second to test the post type.
a) Testing the post title.
$this->assertEquals( 'Hello World', get_the_title( $post_id ) );
Here we have pass the first parameter Hello World and second parameter get_the_title( $post_id ).
The function $this->factory->post->create()
return the $post_id
.
So, We can use The function get_the_title() to get the post Id of the provided post ID.
The function return the Hello World
so our case will pass.
b) Testing the post type
$this->assertEquals( 'post', get_post_type( $post_id ) );
Same as post title we have test the post type of the post with the post ID.
Here we have pass the first parameter post
and second get_the_title( $post_id )
which return the post type.
This tast case also pass.
Refer the complete code as below.
<?php /** * Class SampleTest * * @package Sample_Plugin */ /** * Sample test case. */ class SampleTest extends WP_UnitTestCase { /** * A single example test. */ public function test_sample() { // Replace this with some actual testing code. $this->assertTrue( true ); } /** * Create a new post */ public function test_create_new_post() { $post_id = $this->factory->post->create( array( 'post_title' => 'Hello World', 'post_type' => 'post', ) ); // Check the expected post title. $this->assertEquals( 'Hello World', get_the_title( $post_id ) ); // Check the expected post type. $this->assertEquals( 'post', get_post_type( $post_id ) ); } }
Lets execute the phpunit
command.
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. .. 2 / 2 (100%) Time: 15.26 seconds, Memory: 38.00 MB OK (2 tests, 3 assertions)
Screenshot –

Obviously the function get_the_title() and get_post_type() return the actual stored values.
Now, Lets think that our plugin Sample Plugin use the filter the_title
to modify the post title.
Copy below code and paste into the file wp-content\plugins\sample-plugin\sample-plugin.php
<?php /** * Plugin name: Sample Plugin */ add_filter( 'the_title', function( $post_title = '' ) { return 'Prefix ' . $post_title; });
Here, I have used filter the_title
which add the string Prefix
at the start of the post title.
Now, Lets execute our phpunit
command.
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. .F 2 / 2 (100%) Time: 15.87 seconds, Memory: 38.00 MB There was 1 failure: 1) SampleTest::test_create_new_post Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'Hello World' +'Prefix Hello World' C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin\tests\test-sample.php:34 FAILURES! Tests: 2, Assertions: 2, Failures: 1.
Here, Out PHPUnit test case failed. Becuase, Our plugin filter the post title and concate the string Prefix at the start of the post tile.
So, Our test case is failed.
Now, To make our test case work as expected.
Change the string:
$this->assertEquals( 'Hello World', get_the_title( $post_id ) );
With
$this->assertEquals( 'Prefix Hello World', get_the_title( $post_id ) );
And then execute the phpunit
C:\xampp\htdocs\dev\wp-content\plugins\sample-plugin λ phpunit Installing... Running as single site... To run multisite, use -c tests/phpunit/multisite.xml Not running ajax tests. To execute these, use --group ajax. Not running ms-files tests. To execute these, use --group ms-files. Not running external-http tests. To execute these, use --group external-http. PHPUnit 7.5.20 by Sebastian Bergmann and contributors. .. 2 / 2 (100%) Time: 16.01 seconds, Memory: 38.00 MB OK (2 tests, 3 assertions)
Screesnhot

Now, Our PHP unit test succeed.
Let’s try to implement PHP unit test for your plugin.