Going functional.
Filling in the blanks
I’ve left the earlier post code in an incomplete state, just stubbing out the auxiliary classes needed to register a click on a button.
After a little red and green light juggle I’ve developed the AuthHandler
and CommentsRepository
classes; the code for the classes being here and here.
The test code for the classes is nothing fancy (here the AuthHandlerTest
class and here the code for the CommentsRepositoryTest
one).
Decoupling purpose
Delegating the tasks of the ButtonClickHandler
class to further classes has the purpose of decoupling the code in each class from changes that could happen down the line and be not easy to spot now.
The auth process, now happening using a simple nonce verification, might happen using OAuth in the future and a change in the authentication process should trigger a change in the class dealing with authentication only and not in any class requiring authentication.
On the same path how the comment insertion happens, and if it happens, should be free to change without requiring any class that might need to add a comment to change.
This decoupling purpose, a mere adherence to the single responsibility principle, leads to the following test.
Functional
I’ve put tests for the classes I’ve developed until now in a Codeception suite called wpunit
that, while requiring WordPress to run, is dedicated to the test of a single class per test case.
But what about how those classes fit and work together? Functional testing is meant to exercise a chain of classes to make sure the units can work when integrated in the system (hence the alternate name “integration testing”).
The only meaningful chain I can test now is the one starting with the ButtonClickHandler
class when the /idlikethis/v1/button-click
endpoint is hit.
To do so I will scaffold a new functional test
wpcept generate:wpunit functional "idlikethis\Endpoints\ButtonClickPostRequest"
and have wp-browser scaffold the test for me.
The command itself I’ve used deserves some explanation:
wpcept
- the wp-browser CLI utilitygenerate:wpunit
- generate a test case that will load WordPress in the same global space as the one the tests will run into; I will use WordPress defined methods and functionsfunctional
- add the test to the functional suite
The functional suite configuration file, functional.suite.yml
, will tell Codeception to include WPLoader
module
# Codeception Test Suite Configuration
# suite for WordPress functional tests.
# Emulate web requests and make application process them.
class_name: FunctionalTester
modules:
enabled: [Filesystem, WPLoader, \Helper\Functional]
and telling Codeception to rebuild the test support classes will complete the cycle
codecept build
Getting the head of the chain
To test something I have to control its inputs and outputs and this means I need to get hold of a ready to run idlikethis_Endpoints_ButtonClickHandler
class instance in my test.
The bootstrap.php
file in the plugin root is taking care of that and modifying it to return the prepared container instance will allow me use a context aware and ready to run ButtonClickHandler
instance in the tests.
Here is the modified bootstrap.php
file
<?php
$container = new tad_DI52_Container();
$container->register('idlikethis_ServiceProviders_Shortcodes');
$container->register('idlikethis_ServiceProviders_Endpoints');
return $container;
and the test case I’ve just scaffolded will use that in the setUpBeforeClass
method
<?php
namespace idlikethis\Endpoints;
class ButtonClickPostRequestTest extends \Codeception\TestCase\WPTestCase
{
/**
* @var \tad_DI52_Container
*/
protected static $container;
public static function setUpBeforeClass()
{
self::$container = include codecept_root_dir('bootstrap.php');
return parent::setUpBeforeClass(); // TODO: Change the autogenerated stub
}
public function setUp()
{
// before
parent::setUp();
// your set up methods here
}
public function tearDown()
{
// your tear down methods here
// then
parent::tearDown();
}
}
Testing the endpoint
The full, and lengthy, test class code is here but I’ve pasted a small glimpse of it below
<?php
namespace idlikethis\Endpoints;
class ButtonClickPostRequestTest extends \Codeception\TestCase\WPTestCase
{
/**
* @var \tad_DI52_Container
*/
protected static $container;
public static function setUpBeforeClass()
{
self::$container = include codecept_root_dir('bootstrap.php');
return parent::setUpBeforeClass(); // TODO: Change the autogenerated stub
}
public function setUp()
{
// before
parent::setUp();
// your set up methods here
}
public function tearDown()
{
// your tear down methods here
// then
parent::tearDown();
}
/**
* @test
* it should not insert any comment if auth is not set in POST request
*/
public function it_should_not_insert_any_comment_if_auth_is_not_set_in_post_request()
{
$nonce = wp_create_nonce('button-click');
$_POST['content'] = 'some content';
$post_id = $this->factory()->post->create();
$_POST['post_id'] = $post_id;
/** @var idlikethis_Endpoints_ButtonClickHandlerInterface $endpoint */
$endpoint = self::$container->make('idlikethis_Endpoints_ButtonClickHandlerInterface');
/** @var \WP_REST_Response $out */
$out = $endpoint->handle();
$this->assertInstanceOf('WP_REST_Response', $out);
$this->assertEquals(403, $out->get_status());
$this->assertCount(0, get_comments(['comment_type' => 'idlikethis']));
}
}
While this test might seem a duplicate of the one testing the endpoint there is a marked difference where I’m not mocking or replacing any dependency but manipulating the request parameters.
This is important when future changes are factored in.
Say in a future iteration I will send a notification of sort to a post author when the “I’d like this” comments count reaches a certain threshold: which class should change its implementation? The answer is none of the currently developed ones.
I will refactor some code in the CommentsRepository
class, extend it with the NotifyingCommentsRepository
class, in that I will just override the part of the add_for_post
method that will count the already present comments to send a notification when the threshold is hit.
The tests for each one of the involved classes will not change and I will add a test method to the ButtonClickPostRequestTest
functional test case class to assert a notification has been sent. Or simply create a new functional test case class.
Next
Time to go on the front-end and send the first request.