In this lesson we’ll go through some fundamental test cases using commonly used features in Plugins and themes.
Note: All the test cases use minimal code for simplicity, it should be taken as a conceptual reference.
WP PHPUnit is a framework which is used as a utility tool write test cases related WordPress environment.
The WordPress PHPUnit Test Suite contains thousands of automated tests. Automated tests are small bits of code that verify a specific piece of WP functionality. These tests are a powerful tool both for feature development and for the prevention of regressions.
When phpunit is invoked, the test suite runs a script that sets up a default installation of WordPress, with a configuration similar to what you get with the GUI install. Before any tests are run, the following steps take place:
The Following are reset to original state
Test Database
WordPress keeps both data (posts, users) and state (options) in the database. And the structure of WordPress is such that it’s almost impossible to mock fixtures and settings without actually using a database. As such, the test suite does use a MySQL database for setting up the WP application and for storing fixtures and other data.
It’s important to distinguish between persistent and non-persistent database content in the test suite. When phpunit is invoked, the test suite wipes the test database clean and performs a clean installation. This data – such as the default content of wp_options – is persistent through the tests.
Database modifications made during the test, on the other hand, are not persistent. Before each test, the suite opens a MySQL transaction (START TRANSACTION) with autocommit disabled, and at the end of each test the transaction is rolled back (ROLLBACK). This means that database operations performed from within a test, such as the creation of test fixtures, are discarded after each test. For more information on transactions, see the official MySQL documentation, and especially the section on statements that trigger commits.
class Simple_TestCase extends WP_UnitTestCase { } |
WP_UnitTestCase is an abstract class which inherits all the testing utility methods from WP_PHPUnit Framework and PHPUnit Framework.
We need to extend our TestCass class inorder to utilize those methods using $this keyword.
These annotations as exclusive to WP_PHPUhit
PHP Factory classes are provided by WP PHPUnit. It is used as a Utility class to create posts, users, terms, categories etc in the test environment while running testing.
As every test run deletes the database and uses a complete new database, it is very convenient to create posts using Factory classes.
The factory has the following properties that you can use:
Examples for creating users:
$user_id = $this->factory->user->create(); |
Example for creating posts:
$post_id = $this->factory->post->create( array( ‘post_author’ => $user_id ) ); |
They may all be used in the same manner as demonstrated in the above example with the $user / $post factory.
You can learn more about factory classes here:
Note: This is the source code for factory classes, the code is readable and self-explanatory.
In this lesson, we’ll go through the basics of testing Custom Post Types. We need to test the following:
There are lot of possibilities of tests cases, but it is important keep the test cases to a minimum and to the point.
As a best practice, it is better to include test cases which have an important business logic, feature, behavior etc so that it’s functionality or behavior will not be altered unintentionally while the development as the tests will fail if it happens.
<?php namespace RTC\\Tests\\Plugin; use WP_UnitTestCase; class Test_All_Post_Types extends WP_UnitTestCase { public function test_rtc_movie() { $post_type = get_post_type_object( ‘cpt’ ); $this->assertTrue( post_type_exists( ‘cpt’ ) ); $this->assertSame( ‘CPT’, $post_type->labels->name ); $this->assertSame( ‘cpt’, $post_type->rest_base ); $this->assertSame( true, $post_type->public ); $this->assertSame( array( ‘slug’ => ‘cpt/%taxonomy%/%postname%-%post_id%’, ‘with_front’ => false, ), $post_type->rewrite ); } } |
This is similar to testing custom post types:
<?php namespace RTC\Tests\Plugin; use WP_UnitTestCase; class Test_All_Taxonomies extends WP_UnitTestCase { public function test_rtc_movie() { $taxonomy = get_taxonomy( ‘taxonomy’ ); $this->assertTrue(taxonomy_exists(‘taxonomy’)); } } |
Let’s test a basic shortcode in this example:
Things to test:
function register_shortcode() { add_shortcode( ‘test_shortcode’, ‘example_shortcode’ ); } add_action(‘init’, ‘register_shortcode’); function example_shortcode($atts, $content) { return ‘Works’; } |
In the first test case we are trying to test if the shortcode exists.
In the second test case we are checking the markup of the shortcode
<?php namespace RTC\\Tests\\Plugin; use WP_UnitTestCase; class Test_Shortcodes extends WP_UnitTestCase { public function setUp(): void { parent::setUp(); } public function test_shortcode() { $this->assertTrue( shortcode_exists( ‘test_shortcode’ ) ); } public function test_markup() { $markup = do_shortcode( ‘[test_shortcode]’ ); $this->assertSame( ‘Works’, $markup ); } } |
Advanced testing:
If the shortcode take parameters, we can test it using expected results passing the arguments.
Let’s a add a custom rest endpoint which sends a string as a response.
<?php add_action( ‘rest_api_init’, ‘custom_rest_route’ ); function custom_rest_route() { register_rest_route( ‘foo’, ‘/foo’, array( ‘methods’ => ‘GET’, ‘callback’ => ‘custom_rest_response’, ) ); } function custom_rest_response() { return ‘test’; } |
In this test case we are adding a fixture $this->server. We need this in order to replicate the wp_rest_server global variable.
We initialize REST server using do_action( ‘rest_api_init’ );
You can send a REST request using $response = $this->server->dispatch( $request );
Things to test:
<?php namespace RTC\\Tests\\Plugin; use WP_REST_Request; use WP_UnitTestCase; /** * @property \\WP_REST_Server $server */ class Test_Rest_Endpoints extends WP_UnitTestCase { /** @var WP_REST_Server $wp_rest_server */ private $server; public function setUp(): void { global $wp_rest_server; $this->server = $wp_rest_server = new \\WP_REST_Server(); do_action( ‘rest_api_init’ ); parent::setUp(); // TODO: Change the autogenerated stub } public function test_route_registration() { $namespaces = $this->server->get_namespaces(); $this->assertTrue( in_array( ‘foo’, $namespaces, true ) ); $custom_routes = $this->server->get_routes( ‘foo’ ); $this->assertArrayHasKey( ‘/foo/foo’, $custom_routes ); } public function test_response() { $request = new WP_REST_Request( ‘GET’, ‘/foo/foo’ ); $response = $this->server->dispatch( $request ); $expected = ‘test’; $this->assertEquals( $expected, $response->data ); } } |
More test cases:
Writing test cases for custom classes depends on the functionality.
If there is an important functionality or logic, we need to write test cases for them as a priority.
class My_Custom_Class { public function custom_logic( $a, $b ) { return $a + $b; } } |
Test case to check the expected results of custom_logic()
You can also check important theme info details like version, Theme URI etc.
You can switch to current theme using switch_theme()
Get the WP_Theme object by using wp_get_theme();
Using the object’s getter function you can get theme details.
<?php namespace RTC\\Tests\\Plugin; use WP_UnitTestCase; class Test_Theme_Info extends WP_UnitTestCase { public function test_theme_info() { switch_theme( ‘twentytwentyone-child’ ); $theme_info = wp_get_theme(); $this->assertSame( ‘1.0.0’, $theme_info->get( ‘Version’ ) ); $this->assertSame( ‘rtCamp’, $theme_info->get( ‘Author’ ) ); $this->assertSame( ‘twentytwentyone-child’, $theme_info->get_stylesheet() ); } } |