QA Thing – test-driven developing it 02

Passing the first acceptance test.

The code so far

This post follows a previous one where I laid the testing base to “grow an object-oriented WordPress plugin guided by tests” loosely following along the steps of my re-reading of the “Growing Object-Oriented Software Guided by Tests” book.
The code I’m showing can be found on GitHub.
This is my take on it and I share it as a learning process myself: no absolute truths here.

What needs to happen to pass the (only) test?

The first acceptance test I’ve put in place tries to assert the following:

Visiting the plugin (QA Thing) options page I should see 3 available configurations to apply.

In Codeception terms:

<?php
$I = new AcceptanceTester( $scenario );

$I->am( 'a QA person' );
$I->wantTo( 'see plugin provided configurations in the QA options page' );

$I->loginAsAdmin();
$I->amOnAdminPage( '/admin.php?page=qa-options' );

// The QA Thing plugin itself provides them
$I->canSeeNumberOfElements( '#qa-configuration option', 3 );

Since the plugin code is currently empty the first step is to create a plugin options page.
I’m using DI52 to keep the code efficient and organized; I will give a brief description of what is happening but should that not be enough more can be found on the package repository.
Starting from the plugin main file I’m delegating any operation related to the options page registration to the qa_ServiceProviders_Options class:

/**
 * Plugin Name: QA Thing
 * Plugin URI: http://theAverageDev.com
 * Description: WordPress QA for the world.
 * Version: 1.0
 * Author: theAverageDev
 * Author URI: http://theAverageDev.com
 * License: GPL 2.0
 */

include dirname( __FILE__ ) . '/vendor/autoload_52.php';

$container = new tad_DI52_Container();

$container->register('qa_ServiceProviders_Options');

That class is in turn taking care of registering the moving pieces needed to add an options page in WordPress:

class qa_ServiceProviders_Options extends tad_DI52_ServiceProvider {

    /**
     * Binds and sets up implementations.
     */
    public function register() {
        // seta var on the container to share it among other service providers
        $this->container['options.page-slug'] = 'qa-options';

        // bind a concrete implementation to the the interface common to all option pages
        $this->container->singleton( 'qa_Options_PageI', $this->container->instance( 'qa_Options_Page', array( 'options.page-slug' ) ) );

        // hook the options page registration
        add_action( 'admin_menu', array( $this, 'addOptionsPage' ) );
    }

    /**
     * Adds the options page to the menu.
     */
    public function addOptionsPage() {
        add_menu_page(
            __( 'QA Thing', 'qa' ),
            __( 'QA Thing', 'qa' ),
            'manage_options',
            $this->container['options.page-slug'],
            $this->container->callback( 'qa_Options_PageI', 'render' )
        );
    }
}

The service provider taps into two methods provided by the di52 container: the instance method and the callback method.
The first returns a function that will lazily build an instance of the qa_Options_Page with the provided arguments when and if requested while the second will return a function that will lazily build the current implementation of the qa_Options_PageI interface and call the render method on it; again: when requested.
If the process seems convoluted I recommend reading the series of posts about version 2.0 of DI52(here, here, here, here, here and here) or the DI container documentation as a minimum.
Finally I’m putting in place the bare-minimum code to render the page in the qa_Options_Page class:

class qa_Options_Page implements qa__Options_PageI {
    /**
     * @var string
     */
    protected $slug;

    public function __construct( $slug ) {
        $this->slug = $slug;
    }

    public function render() {
        echo "Hello world";
    }
}

See this version of the code.

Time to re-run the tests again

I run the only acceptance test that’s in place and still see it fail.
Yet it’s failing one step further: good, I’m making progress!

[caption id=“attachment_3266” align=“aligncenter” width=“1153”]Failing one step forward Failing one step forward[/caption]

The next step is to, somewhat, satisfy the following assertion:

$I->canSeeNumberOfElements( '#qa-configuration option', 3 );

Well, that can be done adding some markup to the qa_Options_Page::render() method:

public function render() {
    ?>
    <select name="qa-configuration" id="qa-configuration">
        <option value="foo" class="qa-configuration-option">Foo</option>
        <option value="baz" class="qa-configuration-option">Baz</option>
        <option value="bar" class="qa-configuration-option">Bar</option>
    </select>
    <?php
}

And here it is the passing test in full colors:

[caption id=“attachment_3267” align=“aligncenter” width=“1153”]Finally passing the first acceptance test Finally passing the first acceptance test[/caption]

Isn’t this cheating?

The qa_Options_Page::render() method is just displaying some sudo HTML to pass the test: isn’t this silly?
It is now; it will not be in the future.
This stupid test lays the base for regression testing (“let’s make sure that we are not breaking existing functionality while we put in the new one”) and shows a fundamental concept of test-driven development (TDD): ask for, and stick to, clear requirements.
In a way the code put in place so far does satisfy the only two requirements explicated in the acceptance test: have an options page and three options in it.
Want better code? Write more specific requirements.

And unit testing?

So far I’ve not written a single line of unit tests (or integration tests; what WordPress calls “unit tests” really are “integration tests”).
Isn’t unit testing the foundation of TDD?!
It is, but the rule of avoiding the wheel re-invention applies: test your code.
So what should I test so far? Let’s go file by file and see:

  • the qa-thing.php file, aka “the main plugin file”, builds the DI container and registers a service provider on it; do I own that code? Nope, DI52 has its bases covered with tests already. So nothing to test here so far.
  • the ServiceProviders/qa_ServiceProviders_Options.php file calls more methods on the container (instance, callback, singleton, offsetSet using the ArrayAccess interface) which I do not own. It then it calls add_action and, eventually, add_menu_page… I do not own that code either: I know WordPress code works.
  • the Options/Page.php file calls an incredibly dumb __construct and then… the render method prints some HTML to the page.

Should I write a test for the render method? I think not.
The method does not contain conditionals and its current output is fixed: at this point it does not make sense to me.
Will I test this in the future? Probably as complexity increases but not now.

Lazy is the keyword

The most difficult part of TDD is understanding what should be tested, how it should be tested and how much it should be tested.
That’s not exact science but I’ve outlined above some “rule of thumbs” I use:

  1. use other people’s tested code
  2. use your already tested code
  3. if you really have to: write as little code as possible
  4. write “dumb” code where possible
  5. code becomes “smart” if it makes choices (conditionals)
  6. test “smart” code
  7. don’t fret the test coverage
  8. write re-usable code: you want to write it and test it once and reuse it over and over (see 2)

Next

I will write a new acceptance test to force me to write some code that’s actually doing something and put in the place the minimum required code and related tests to satisfy it.