Developing a plugin using DI and TDD 08

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 utility
  • generate: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 functions
  • functional - 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.