Tusk: A PHP Testing Framework for Elephants

Right, this is the first non-toy project I’ve actually released, so bear with me. I am pleased to announce Tusk (working name) version 0.1, a BDD-style testing framework for PHP. Tusk is heavily based on the JavaScript testing framework Jasmine, although there have been some necessary changes to make it more ‘PHP-friendly’.

The largest of these is to deal with the way PHP handles (or, rather, doesn’t handle) variable scoping. Unlike JavaScript, which has (very nearly) lexical scoping, PHP, outside of classes, has only local and global scope for variables. If you want to put your tests/specs inside ‘blocks’ (i.e. anonymous functions, as is the way with Jasmine, RSpec and friends, and you want to use variables from outside that scope, you have to explicitly specify this with PHP’s use keyword. Messy, to say the least, as our take on the classic ‘Bowling’ BDD test example shows:

<?php

describe('Bowling', function() {
    $bowling = null;

    beforeEach(function() use (&$bowling) {
        $bowling = new Bowling();
    });

    describe('getScore()', function() use (&$bowling) {
        it('returns 0 for an all gutter game', function() use(
            &$bowling
        ) {
            for ($i = 0; $i < 10; ++$i) {
                $bowling->hit(0);
            }

            expect($bowling->getScore())->toBe(0);
        });
    });
});

Not only do we need to specify the $bowling variable three times in three separate use statements, but we have to remember to pass it by reference as well. We also have to explicitly assign a null value to the variable before we can start passing it around, or the PHP interpreter will complain that $bowling doesn’t exist. This, no doubt, would get old fast and has nothing like the elegance of Jasmine specs.

Fortunately, PHP 5.4 comes to the rescue. Unlike PHP 5.3, where anonymous functions were first introduced, PHP 5.4 supports the use of the $this variable inside a closure, and it can be bound to anything you like. Thanks to that, in Tusk, if we assign a value to a $this->bowling variable in our beforeEach block, we can access that same value later within our it block. No more use statements!

<?php

describe('Bowling', function() {
    beforeEach(function() {
        $this->bowling = new Bowling();
    });

    describe('getScore()', function() {
        it('returns 0 for an all gutter game', function() {
            for ($i = 0; $i < 10; ++$i) {
                $this->bowling->hit(0);
            }

            expect($this->bowling->getScore())->toBe(0);
        });
    });
});

Expectations work almost the same way as in Jasmine, and each can be negated by preceding it with the word ‘not’, e.g. ‘toBe’ becomes ‘notToBe’. Tusk comes with a variety of default matchers and supports adding custom ones to your heart’s content. The Tusk Github page has further documentation.

If you want to use mocks (and you should), Tusk plays very nicely with Mockery, which is a very powerful mocking library and allows mock objects to be specified much more concisely than PHPUnit. Just remember the Mockery::close() call in your afterEach block.

For the 0.2 release, I will be looking at integration with Xdebug code coverage, as well as a few other general improvements.

Tusk is available on Packagist. After installing via Composer, just run the tusk binary with the path(s) to your specs as arguments:

./vendor/bin/tusk my_specs/ some_more_specs/ even_more/

If all goes well, you should get something that looks like this:

44 specs, 0 failed, 0 skipped

Happy speccing!