The document discusses the importance of test coverage for WordPress REST API projects. It explains that test coverage ensures security and reliability by explicitly testing that private information is not disclosed, unauthorized requests are not permitted, and consistent responses are returned. The document provides examples of unit and integration tests for a phone number formatting function and REST API endpoint. It emphasizes that test-driven development and continuous integration helps catch errors and security issues early.
Call me @ 9892124323 Cheap Rate Call Girls in Vashi with Real Photo 100% Secure
Â
Test Coverage for Your WP REST API Project
1. Ensure Security and Reliability with Test Coverage
Test Coverage for Your
WP REST API Project
2. Daniel Bachhuber, Author
Daniel Bachhuber knows a thing or two about
WordPress.
In addition to working on the WP REST API,
Bachhuber founded Handbuilt, a shop
providing WordPress development and
consulting services. He also founded
Runcommand and is an active maintainer of
the WP-CLI.
3. Daniel Bachhuber, Author
Bachhuber wrote this tutorial to help
developers working with the WP REST API
ensure a secure, performant site. Whether you
are currently working on a REST API project or
not, check out Danielâs tips for securing
endpoints as you go.
4. Alex the developer is pretty excited about the WordPress REST API. Because the
infrastructural components were introduced in WordPress 4.4, they too can use
register_rest_route() to easily register their own WP REST API endpoints. In
fact, they love registering routes so much that theyâre creating API endpoints for every
project they work on.
Sound like you too? Are you writing full test coverage for your endpoints as you go? If
not, you absolutely need to be, for two primary reasons: security and reliability. If you
arenât writing test coverage for your endpoints, sorry Charlieâyour endpoints are
probably insecure, and probably behave unexpectedly for clients.
This tutorial is everything you need to get started.
Ensure Security and Reliability
5. To start at the beginning, âwriting testsâ is a
way for you, as the developer of a complex
application, to define assertions of how the
applicationâs functionality is expected to
work.
Pairing your tests with a continuous
integration system like Travis CI means your
suite of tests will be run automatically on
every push or pull request, making it much
easier to incorporate tests into your
development workflow.
What Are We Talking About?
6. As it relates to your WP REST API
endpoints, there are two common ways
to think about test coverage.
⢠âUnit testsâ test the smallest testable
part of your application (e.g. the
phone formatting function in this
tutorial).
⢠âIntegration testsâ test groups of
application functionality (e.g. the WP
REST API endpoints in this tutorial).
What Are We Talking About?
7. Invest in Security and Performance
Test coverage is additive; the only place to start is at the very beginning. Continual
investment over time leads to an increasing amount of test coverage, and greater
confidence that your application isnât breaking unexpectedly as it becomes more
complex.
Say, for instance, youâve written a rad_format_phone_number( $input )
function to format phone numbers within your WordPress application. Your first pass
at the function produces something like this:
function rad_format_phone_number( $input ) {
$bits = explode( '-', $input );
return "({$bits[0]}) {$bits[1]}-{$bits[2]}";
}
8. To ensure the function works as expected, you write a test case for it like this:
You run phpunit to see if the test passesâand it does!
Invest in Security and Performance
function test_format_phone_number() {
$this->assertEquals( '(555) 212-
2121', rad_format_phone_number( '555-212-2121' ) );
}
9. Test-Driven Development
What if a user passes a value like 5552122121 or +1 (555) 212 2121? Or even an empty
string? Make sure your function can handle these alternative formats, as well as the
original input format you created the function for.
Using Test-Driven Development, you can actually write the test cases first, and then
adapt your function until the tests pass.
function test_format_phone_number() {
$this->assertEquals( '(555) 212-2121', rad_format_phone_number( '555-212-2121' ) );
$this->assertEquals( '(555) 212-2121', rad_format_phone_number( '5552122121' ) );
$this->assertEquals( '(555) 212-2121', rad_format_phone_number( '+1 (555) 212 2121' ) );
$this->assertEquals( '', rad_format_phone_number( '' ) );
}
10. Twenty minutes of regex later, youâve created a function to handle the assertions above:
Congratulations! Youâve introduced test coverage into your code.
Test-Driven Development
function rad_format_phone_number( $input ) {
if ( preg_match( '#([d]{3})[^d]*([d]{3})[^d]*([d]{4})#', $input,
$matches ) ) {
return "({$matches[1]}) {$matches[2]}-{$matches[3]}";
}
return '';
}
11. Why Test Coverage Is Even More
Important with a WP REST API Project
Test Coverage for Your WP REST API Project
12. Why Is it More Important?
Because the WP REST API offers a direct
read/write interface into WordPress, you need
to make absolutely sure you:
⢠Arenât unintentionally disclosing private
information to unauthorized requests.
⢠Arenât unintentionally permitting
unauthorized requests to perform write
operations on your application.
13. You may be manually verifying the security of
your endpoints while building your WordPress-
based application, but test coverage enables you
to make those security assertions explicit.
Furthermore, even if your WP REST API endpoints
are read-only and donât deal with private
information, you want to make sure your
application returns consistent responses. The
clients built on top of your API expect consistent
responses above all elseâand can break
unexpectedly when they receive unexpected
data.
Why Is it More Important?
14. How Should I Write My Endpoints?
If youâre familiar with PHPUnit and the WordPress projectâs PHPUnit test suite, then youâre
already part of the way there. If youâre not, youâll want to get yourself up to speed, and
then come back to this tutorial. You can also open the entire test class in a separate tab if
youâd like to refer to it as we go along.
15. How Should I Write My Endpoints?
To make it possible to test your registered WP REST API endpoint in a PHPUnit test, youâll
need to first set up a WP_REST_Server instance for your test class. If you just have one
test class, you can perform this step in the Tests_REST_API_Demo::setUp() method:
public function setUp() {
parent::setUp();
global $wp_rest_server;
$this->server = $wp_rest_server = new WP_REST_Server;
do_action( 'rest_api_init' );
}
16. The call to rest_api_init ensures your routes are registered to the server
within the test. Make sure you also reset the $wp_rest_server global on
Tests_REST_API_Demo::tearDown():
How Should I Write My Endpoints?
public function tearDown() {
parent::tearDown();
global $wp_rest_server;
$wp_rest_server = null;
}
17. Letâs imagine we want to make this phone number accessible through the WP REST API.
However, because a phone number is semi-private information, it should only editable by
administrators.
How Should I Write My Endpoints?
register_rest_route( 'rad/v1', 'site-info', array(
array(
'methods' => 'GET',
'callback' => function( $request ) {
return array(
'phone_number' => get_option( 'phone_number' )
,
);
},
), Click for the full code.
Switching to the
plugin file, our
first attempt at
registering our
WP REST API
endpoint looks
like this:
18. Because we have $thisâserver available on our test class, we can create a
WP_REST_Request object, dispatch it on WP_REST_Server, inspect what the server
includes on WP_REST_Response.
How Should I Write My Endpoints?
public function test_get() {
$request = new WP_REST_Request( 'GET', '/rad/v1/site
-info' );
$response = $this->server->dispatch( $request );
$this->assertResponseStatus( 200, $response );
$this->assertResponseData( array(
'phone_number' => '(555) 212-2121',
), $response );
}
In this example,
notice how we
test both the
response data and
the response
status.
Click for the full code.
19. Clients interpret HTTP status codes to have a higher-level
understanding of the type of response, so we want to also make sure
weâre returning the proper status code.
How Should I Write My Endpoints?
public function test_get() {
$request = new WP_REST_Request( 'GET', '/rad/v1/site
-info' );
$response = $this->server->dispatch( $request );
$this->assertResponseStatus( 200, $response );
$this->assertResponseData( array(
'phone_number' => '(555) 212-2121',
), $response );
} Click for the full code.
20. Uh oh! If the warning bells arenât going off already, the endpoint weâve registered is
hugely insecureâany request, including logged-in and logged-out users can both read
or update our phone number. We need to patch this right away.
How Should I Write My Endpoints?
public function test_get_unauthorized() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'GET', '/rad/v1/site-
info' );
$response = $this->server->dispatch( $request );
$this->assertResponseStatus( 401, $response );
}
Click for the full code.
21. Because weâre practicing Test-Driven Development, we first write failing tests
(changeset) for the security vulnerability (see the actual pull request on Github). Our
tests of our WP REST API endpoints now look like this.
How Should I Write My Endpoints?
public function test_get_unauthorized() {
wp_set_current_user( 0 );
$request = new WP_REST_Request( 'GET', '/rad/v1/site-
info' );
$response = $this->server->dispatch( $request );
$this->assertResponseStatus( 401, $response );
}
Click for the full code.
22. A Few Key Details to Note
⢠wp_set_current_user() lets us set the scope of the test to a given user that
already exists. Because our tests are against the endpoint itself, and not the
authentication system WordPress uses to verify the response, we can safely assume
the current user within the scope of the code is the actual user making the request.
If authentication fails, WordPress will wp_set_current_user( 0 );, which is
functionally equivalent to a logged out request.
23. ⢠Itâs incredibly important to take to heart the difference between authentication
and authorization. Authentication refers to whether or not a request is
associated with a valid user in the system. Authorization refers to whether or
not a given user has permission to perform a given action. Even though a user
may be authenticated, they might not be authorized. Your WP REST API
endpoint should return a 401 when a user isnât authenticated, and a 403 when
a user isnât authorized.
⢠assertResponseStatus() and assertResponseData() are helper
methods you are more than welcome to copy into your own test suite.
A Few Key Details to Note
24. Given our new knowledge about authentication and authorization, we can update our
endpoint to use thepermission_callback to authorize the request before our
callback handles it.
A Few Key Details to Note
add_action( 'rest_api_init', function() {
register_rest_route( 'rad/v1', 'site-info', array(
array(
'methods' => 'GET',
'callback' => function( $request ) {
return array(
'phone_number' => get_option( 'phone_number' )
,
);
}, Click for the full code.
25. To be as helpful as possible to clients, letâs adapt our endpoint to only accept input
when the data is close to a phone number, and ensure our response data is
formatted as a
phone number or
empty string.
A Few Key Details to Note
add_action( 'rest_api_init', function() {
register_rest_route( 'rad/v1', 'site-info', array(
array(
'methods' => 'GET',
'callback' => function( $request ) {
return array(
'phone_number' => get_option( 'phone_number' )
,
);
}, Click for the full code.
26. Again, because weâre practicing Test-Driven Development, we first write failing tests (see
the actual pull request on Github). These failing tests look like this:
A Few Key Details to Note
public function test_get_authorized_reformatted() {
update_option( 'phone_number', '555 555 5555' );
wp_set_current_user( $this->subscriber );
$request = new WP_REST_Request( 'GET', '/rad/v1/site
-info' );
$response = $this->server->dispatch( $request );
$this->assertResponseStatus( 200, $response );
$this->assertResponseData( array(
'phone_number' => '(555) 555-5555',
), $response );
}
Click for the full code.
27. Given our new knowledge about making to sure consistently handle data, we can
update our endpoint to register the phone_number resource argument with a validation
callback, and make sure to return data through our rad_format_phone_number()
function.
A Few Key Details to Note
register_rest_route( 'rad/v1', 'site-info', array(
array(
'methods' => 'GET',
'callback' => function( $request ) {
return array(
'phone_number' => rad_format_phone_number( get_
option( 'phone_number' ) ),
);
}, Click for the full code.
28. This is Only the BeginningâŚ
⢠Test coverage is critically important for two
reasons: security and reliability. You want to make
triply sure your API isnât disclosing private
information, permitting unauthorized operations,
and responds consistently to correct and
incorrect client requests.
⢠Using the WordPress projectâs PHPUnit test suite,
you can write integration tests for your endpoints.
Include assertions for both the response data and
the response status. For every successful request
test you write, include 4 or 5 permutations of
erred requests.
29. ⢠Clients will always send your application
unexpected or incorrect data. If your
endpoints can provide consistent, clear, and
expected responses, then the client
developerâs life will be greatly improved, as
they wonât have to spend hours or days
trying to debug cryptic errors from an
application they donât have access to.
This is Only the BeginningâŚ
30. Run your WP REST API project on
Pantheon.
Weâve created a unique WordPress hosting platform.
We provide elastic hosting and the best cloud-based
development tools for teams.
Try it for free