TDD WordPress project – 03

Where I pick up with TDD WordPress components using Codeception and function-mocker.

A real first test

I have modified the Schedule method signatures to stick with WordPress Cron functions and the up to date starting code in GitHub tagged 0.1.2.
I start testing the simplest method the Schedule class exposes and that’s isScheduled; in the tests/unit/WPSchedule_Schedule_ScheduleTest.php file I add the new method as the last one defined in the test class; I’m adding FunctionMocker::setUp and FunctionMocker::tearDown methods to the test case too in the setUp and tearDown methods:

class WPSchedule_Schedule_ScheduleTest extends \PHPUnit_Framework_TestCase {

    /**
     * @var WPSchedule_Schedule_Schedule
     */
    protected $sut;

    /**
     * @var string
     */
    protected $sutClass = 'WPSchedule_Schedule_Schedule';

    protected function setUp() {
        // first
        FunctionMocker::setUp();
        $this->sut = new WPSchedule_Schedule_Schedule();
    }

    protected function tearDown() {
        // last
        FunctionMocker::tearDown();
    }

    /**
     * @test
     * it should be initializable
     */
    public function it_should_be_initializable() {
        $this->assertInstanceOf( $this->sutClass, $this->sut );
    }

        /**
         * @test
         * it should return false if wp_next_scheduled returns false
         */
        public function it_should_return_false_if_wp_next_scheduled_returns_false()
        {
            // replace the `wp_next_scheduled` function and return `false`
            FunctionMocker::replace('wp_next_scheduled', false);

            $this->assertFalse($this->sut->isScheduled('some_hook'));
        }
}

I also stub the method out in the WPSchedule_Schedule_Schedule class

class WPSchedule_Schedule_Schedule extends WPSchedule_Schedule_AbstractSchedule {
    public function isScheduled($hook, $args = null){

    }
}

and will run the test case to see it fail

$ codecept run unit

First real WPSchedule test fail

I’m adding as small code as possible to make the test pass (will skip the return false; part though) and will modify the isScheduled method to

class WPSchedule_Schedule_Schedule extends WPSchedule_Schedule_AbstractSchedule {
    public function isScheduled($hook, $args = null){
        return wp_next_scheduled( $hook, $args );            
    }
}

Running the test again will yield a success First WPSchedule real test pass

More tests

Without going into that much detail I will add more tests and, given the simplicity of the method under test, will not need any more modification to it to watch it pass:

use tad\FunctionMocker\FunctionMocker;

class WPSchedule_Schedule_ScheduleTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @var WPSchedule_Schedule_Schedule
     */
    protected $sut;

    /**
     * @var string
     */
    protected $sutClass = 'WPSchedule_Schedule_Schedule';

    protected function setUp()
    {

        // first
        FunctionMocker::setUp();
        $this->sut = new WPSchedule_Schedule_Schedule();
    }

    protected function tearDown()
    {

        // last
        FunctionMocker::tearDown();
    }

    /**
     * @test
     * it should be initializable
     */
    public function it_should_be_initializable()
    {
        $this->assertInstanceOf($this->sutClass, $this->sut);
    }

    /**
     * @test
     * it should return false if wp_next_scheduled returns false
     */
    public function it_should_return_false_if_wp_next_scheduled_returns_false()
    {
        FunctionMocker::replace('wp_next_scheduled', false);

        $this->assertFalse($this->sut->isScheduled('some_hook'));
    }

    /**
     * @test
     * it should return true if wp_next_scheduled returns true
     */
    public function it_should_return_true_if_wp_next_scheduled_returns_true()
    {
        FunctionMocker::replace('wp_next_scheduled', true);

        $this->assertTrue($this->sut->isScheduled('some_hook'));
    }

    /**
     * @test
     * it should return false if wp_next_scheduled returns false with args
     */
    public function it_should_return_false_if_wp_next_scheduled_returns_false_with_args()
    {
        FunctionMocker::replace('wp_next_scheduled', function ($hook, $args)
        {
            return $args !== ['some', 'args'];
        });

        $this->assertFalse($this->sut->isScheduled('some_hook', ['some', 'args']));
        $this->assertTrue($this->sut->isScheduled('some_hook'));
    }

        /**
         * @test
         * it should return true if wp_next_scheduled returns true with args
         */
        public function it_should_return_true_if_wp_next_scheduled_returns_true_with_args()
        {
            FunctionMocker::replace('wp_next_scheduled', function ($hook, $args)
            {
                return $args === ['some', 'args'];
            });

            $this->assertTrue($this->sut->isScheduled('some_hook', ['some', 'args']));
            $this->assertFalse($this->sut->isScheduled('some_hook'));
        }
}

0.1.3 tests passing

Some gotchas on function-mocker

Right now, and my hope is it will keep like that, function-mocker comes with an interface as simple as it can get: just the replace method.
The lack of “fanciness” is outweighed by the possibility to use closures to set return values; two of the methods above do just that in a simple way moving the knowledge needed from the one specific to the tool to the one specific to the code.
So: use closures to handle complex return values mapping, those anonymous functions will be passed any argument the called function that’s being replaced will receive.

On GitHub

The code this post ends with can be found on GitHub at tag 0.1.3.