WordPress loader for functional testing - 01

What’s a WordPress loader?

In its most basic functions it’s a class that will include the wp-load.php file and start WordPress in a CLI mode.

Doesn’t one exist already?

Yes, Nikolay Bachiyski made one and that’s been ported into core. Could I do better? No. Can I make it work with and inside Codeception? I hope so.
I will chronicle the brief experiment of my attempt to integrate it in my Codeception workflow for functional scopes.
While I’ve set up some helpers to make testing easier at unit and acceptance level I miss the possibility to test pieces of a theme or plugin.

An example of what I’d like to do

The plugin I’m working on, Route Pages, will perform an operation at activation time (more details here), and that operation is triggered by the plugin activation hook. That’s not what I’d like to happen and would like to move the triggering to some backend UI control (e.g. a “Generate route pages” button).
Since the process involves more than one class I can test the desired behaviour using an acceptance test like this one

<?php

class PageGenerationCest
{
    /**
     * @test
     * it should create a route page if a persisted route meta info is in the db
     */
    public function it_should_create_a_route_page_if_a_persisted_route_meta_info_is_in_the_db(AcceptanceTester $I)
    {
        // Prepare
        // arrays are stored serialized in wp db
        $routeMeta = serialize(array('route-one' => array('title' => 'Route one', 'permalink' => 'route-one', 'generate' => 'page')));
        $I->haveOptionInDatabase(RoutePages_GeneratingRoute::OPTION_ID, $routeMeta);

        // Execute
        $I->loginAsAdmin();
        $I->amOnPluginsPage();
        $I->activatePlugin('wp-router');
        $I->activatePlugin('route-pages');

        // Verify
        $I->amOnPagesPage();
        $I->see('Route one', '.page-title');
    }
}

but the weakness here lies in the assumption that the generation will always be triggered at activation: if I move the triggering mechanism then I will have to rewrite the test.
Since tests tend to proliferate moving that function again in the future might make keeping the tests up to the pace a pain bigger than not testing.

Then mock! Or not?

In the test above I’m mocking, using the haveOptionInDatabase method, a whole lot of functions resulting in such an option and that’s fine: I want to test the business logic of page creation assuming the option is there and is valid.
WordPress plugins do not live alone and mocking each and every input, interaction and “concurrent” access to database and resources a real WordPress site might have would require an immense amount of mocking and researching. So I can mock, yes, but just what I know.
Furthermore I do not want to test a UI I might change. I want to test that in a separate test. If acceptance testing tests the chain and unit testing tests the single chain-ring then I want to test the interaction of just two or three rings.

A use case

I’d like to rewrite the tests above like this

<?php

class PageGenerationCest
{
    /**
     * @test
     * it should create a route page if a persisted route meta info is in the db
     */
    public function it_should_create_a_route_page_if_a_persisted_route_meta_info_is_in_the_db(AcceptanceTester $I)
    {
        // Prepare
        // arrays are stored serialized in wp db
        $routeMeta = serialize(array('route-one' => array('title' => 'Route one', 'permalink' => 'route-one', 'generate' => 'page')));
        $I->haveOptionInDatabase(RoutePages_GeneratingRoute::OPTION_ID, $routeMeta);

        // Execute, this is what happens when route pages creation is triggered
        $pageManager = new RoutePages_PagesManager();
        $pageManager->createRoutePages();

        // Verify
        $I->amOnPagesPage();
        $I->see('Route one', '.page-title');
    }
}

to be able to skip the interaction with the backend UI and keep away from possible changes in it. And having a fully functional, database connected, WordPress installation running means I can skip mocking whole sets of functions and transients and mock just my preconditions.

Next

See what’s needed to do it.