Articles

Mocking Global Php 5.3 Functions Using Namespaces


Introduction

Let's say you'd like to achieve a 100% coverage of your code in your php application (you obsessive geek..). It's almost certain you'll need to start mock'ing things around. So far so good.. but sooner or later you will need to deal with the test cases for the code that use global php functions. How can you mock them? In this article I'll show you a small technique to allow you to mock global php functions. This will require your code to use namespaces, available since PHP 5.3.

Classical example

Suppose this is the code you'd like to test, a file called SomeClass.php:

namespace My\Nice\Namezpace;

class SomeClass
{
    public function doSomething()
    {
        // Notice how the global function "socket_create" is called without
        // the leading backslash (relative instead of absolute call in a
        // namespaced environment). This will let us later mock the call
        // by taking advantage of the relative-to-the-namespace call.
        $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        if ($socket === false) {
            throw new \Exception('Ooops!');
        }
        socket_close($socket);
        return true;
    }

    public function __construct()
    {
    }
}

It's fairly easy to have a unit test for the "success case" by just asserting the result value. What happens with the other use case, where the exception is thrown?
This is an extreme simple example, but what happens if something complicated has to be done when the call to socket_create fails for whatever reason? How can you be sure that the code is behaving like it should be?

The tests

This is our Test_SomeClass.php file:

namespace {
    // This allow us to configure the behavior of the "global mock"
    $mockSocketCreate = false;
}

namespace My\Nice\Namezpace {
    // And this here, does the trick: it will override the socket_create()
    // function in your code *just for the namespace* where you are defining it.
    // This relies on the code above calling the socket_create function without
    // the leading backslash, so we trick SomeClass into calling our own function
    // inside that namespace instead of the global socket_create function.
    function socket_create() {
        global $mockSocketCreate;
        if (isset($mockSocketCreate) && $mockSocketCreate === true) {
            return false;
        } else {
            return call_user_func_array('\socket_create', func_get_args());
        }
    }

include_once './SomeClass.php';

class Test_SomeClass extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        global $mockSocketCreate;
        $mockSocketCreate = false;
    }

    /**
     * This will test the success case.
     * @test
     */
    public function can_success_case()
    {
        $dummy = new \My\Nice\Namezpace\SomeClass;
        $this->assertTrue($dummy->doSomething());
    }

    /**
     * This will enable the mock and call the code. socket_create will now
     * return false instead the call of the original socket_create, our work
     * here is done.
     * @test
     * @expectedException \Exception
     */
    public function cannot_error_case()
    {
        global $mockSocketCreate;
        $mockSocketCreate = true;
        $dummy = new \My\Nice\Namezpace\SomeClass;
        $dummy->doSomething();
        $this->fail('Should not here');
    }
}
}

Running phpunit will give you the 100% coverage :)

phpunit --debug --process-isolation --verbose --stop-on-incomplete --stop-on-skipped --stop-on-failure --stop-on-error --colors --coverage-html html Test_SomeClass.php

Conclusions

With this little trick we can increase the testing level of the code. Most "normal" php code lacks good error handling (differences between cero, false, and null are among the #1's). Now everything can be tested at the highest levels possible, which is a novelty in the php language :) now go get that 100%!