In a real-world scenario, we have to write some custom REST API endpoints to satisfy our business requirements, as one shoe will not fit for all right? So how do we make sure that our custom REST API always works as expected and how to keep them always under the weather? Simple write unit test to cover them all.
Modular programming
It’s good practice to separate our custom functionality from other functionalities. In other term, we should group and separate each functionalities from other code as much as we can. This kind of practice is called modular programming.
Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality.
wikipedia
There are a lot of advantages of following a modular programming practice.
- Efficient code management
- Reusability
- Ease of debugging
- Ease of collaboration
- Readable code
So with all these advantages, lets group our custom functionalities into a WordPress plugin instead of dumping them into the theme code.
Read my previous post regarding An Introduction to unit testing WordPress plugin using PHPUnit to understand and set up a plugin with PHPUnit integrated.
Custom REST API
We have to use register_rest_route()
method to register our custom REST endpoint. resgister_rest_route()
will accept three arguments namespace, route and arguments.
Our sample REST API will look like one below. This will return latest post title and it’s URL based on the author ID. Our final REST route will look like {domain_name}/wp-json/d9/v1/author/1
.
/**
* Registering the new endpoint.
*/
add_action( 'rest_api_init', function () {
register_rest_route( 'd9/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'get_posts_by_author',
'permission_callback' => function () {
return true;
}
) );
} );
/**
* Grab latest posts title and URL by an author!
*
* @param array WP_REST_Request $request Request.
*
* @return array|null array of the latest,
* or null if none.
*/
function get_posts_by_author( $request ) {
$posts = get_posts( array(
'author' => $request['id'],
) );
if ( empty( $posts ) ) {
return null;
}
$results = array();
foreach ( $posts as $post ) {
$results[] = array(
'title' => $post->post_title,
'url' => get_permalink( $post->ID )
);
}
return $results;
}
Sample response will look like below one.
[
{
"title": "Hello world!",
"url": "http://{domain_name}/2020/02/10/hello-world/"
},
{
"title": "Hello world!",
"url": "http://{domain_name}/2020/02/10/hello-world/"
}
]
PHPUnit tests for WP REST API
In order to test the rest endpoint in the unit test, we are not going to get the actual API endpoint and make an HTTP request. Instead, we will use WordPress core WP_REST_Server
and WP_REST_Request
classes to get the REST response.
/**
* Tests for Random_Quote_Wp class
*/
class Test_Posts_By_Author extends WP_UnitTestCase {
/**
* Holds the WP REST Server object
*
* @var WP_REST_Server
*/
private $server;
/**
* Holds user id.
*
* @var int
*/
private $user_id;
/**
* Holds post id.
*
* @var int
*/
private $post_id;
/**
* Create a user and a post for our test.
*/
public function setUp() {
// Initiating the REST API.
global $wp_rest_server;
$this->server = $wp_rest_server = new \WP_REST_Server;
do_action( 'rest_api_init' );
$this->user_id = $this->factory->user->create( array(
'display_name' => 'test_author',
) );
$this->post_id = $this->factory->post->create( [
'post_title' => 'Hello World',
'post_content' => 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
'post_status' => 'publish',
'post_author' => $this->user_id
] );
}
/**
* Delete the user and post after the test.
*/
public function tearDown() {
wp_delete_user( $this->user_id );
wp_delete_post( $this->post_id );
}
/**
* Test the author posts endpoint.
*/
public function test_posts_author_endpoint() {
}
}
In the setUp()
method we have manually initialised the REST Server and manually triggering the rest_api_init
hook. This is important to set up the WordPress Rest API context.
Now let’s write tests to verify the endpoints and it’s value.
/**
* Test the author posts endpoint.
*/
public function test_posts_author_endpoint() {
$request = new WP_REST_Request( 'GET', '/d9/v1/author/' . $this->user_id );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertCount( 1, $data );
$this->assertEquals( 'Hello World', $data[0]['title'] );
$this->assertEquals( get_permalink( $this->post_id ), $data[0]['url'] );
}
Now we need to make a request to WP_REST_Request
with our rest route /d9/v1/author/{user_id}
with GET
context. WP_REST_Server will help us to process the request and get the data. Once we have the data, then we can test them against our requirement.
Now you can run the tests to confirm the fucntionality.
➜ ./vendor/bin/phpunit tests/test-posts-by-author.php 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: 2.22 seconds, Memory: 28.00 MB OK (1 test, 3 assertions)