PHPUnit Bootstrap and Autoloading classes

The PHPUnit Bootstrap is perfect when there is code to be run before tests are executed. The limitation however is there can only be one bootstrap per PHPUnit configuration file.

This is an issue if there are a set of classes that need to be included – we don’t want to manually include every class every test could possibly need.

PHP’s Autoloading feature comes to the rescue, allowing us to seamlessly include classes as they’re required for tests.

The old way

Normally, you would have to include all the dependencies of classes you wish to test:

include_once('../classes/Foo.php');
include_once('../classes/Bar.php');
include_once('../classes/Baz.php');
// ... ad infinitum

class FooTest extends PHPUnit_Framework_TestCase{
    public function testNewFoo() {
        $foo = new Foo();
        // ...
    }
}

The PHPUnit Bootstrap way

Instead, we can let PHPUnit know we want to run a bootstrap file before tests are executed:

<phpunit bootstrap="bootstrap.php">
</phpunit>

Simply save this as phpunit.xml in the same directory as where you run your tests.

The Autoloader

In a file called AutoLoader.php, we’re going to use PHP’s autoloading feature to include classes as they’re needed:

[Note: This requires a certain file-name convention of files. The filename should be the same as the class name (with the .php extension)]

class AutoLoader {

    static private $classNames = array();

    /**
     * Store the filename (sans extension) & full path of all ".php" files found
     */
    public static function registerDirectory($dirName) {

        $di = new DirectoryIterator($dirName);
        foreach ($di as $file) {

            if ($file->isDir() && !$file->isLink() && !$file->isDot()) {
                // recurse into directories other than a few special ones
                self::registerDirectory($file->getPathname());
            } elseif (substr($file->getFilename(), -4) === '.php') {
                // save the class name / path of a .php file found
                $className = substr($file->getFilename(), 0, -4);
                AutoLoader::registerClass($className, $file->getPathname());
            }
        }
    }

    public static function registerClass($className, $fileName) {
        AutoLoader::$classNames[$className] = $fileName;
    }

    public static function loadClass($className) {
        if (isset(AutoLoader::$classNames[$className])) {
            require_once(AutoLoader::$classNames[$className]);
        }
     }

}

spl_autoload_register(array('AutoLoader', 'loadClass'));

Putting it together

Finally, create the bootstrap.php file which will include the autoloader and register your include directory:

include_once('AutoLoader.php');
// Register the directory to your include files
AutoLoader::registerDirectory('../classes');

All done!

Now, your tests no longer have to include the classes you’re after. PHP’s autoloading (and the AutoLoader class) will take care of it for you:

// No includes needed, hooray!
class FooTest extends PHPUnit_Framework_TestCase{
    public function testNewFoo() {
        $foo = new Foo();
        // ...
    }
}
Tagged , , , , ,

10 thoughts on “PHPUnit Bootstrap and Autoloading classes

  1. Maksim says:

    Nice article. It really helps me. But I’ve made little improvement for loading also .class.php files that contain classes more like then in JAVA.
    foreach ($di as $file) {

    if ($file->isDir() && !$file->isLink() && !$file->isDot()) {
    // recurse into directories other than a few special ones
    self::registerDirectory($file->getPathname());

    } elseif (substr($file->getFilename(), -10) === ‘.class.php’) {
    // save the class name / path of a class.php file found
    $className = substr($file->getFilename(), 0, -10);
    echo “Loading -> $className.class.php” . PHP_EOL;
    AutoLoader::registerClass($className, $file->getPathname());
    continue;
    } elseif (substr($file->getFilename(), -4) === ‘.php’) {
    // save the class name / path of a .php file found
    $className = substr($file->getFilename(), 0, -4);
    echo “Loading -> $className.php” . PHP_EOL;
    AutoLoader::registerClass($className, $file->getPathname());

    }
    }

  2. macjohn says:

    There is a shorten way to accomplish this. Php can autoload classes. Just include those feew lines in your bootstrap.php and the autoload classes will do his magic.

    function testAutoloader($className) {
    require_once $class_name . ‘.php’;
    }
    spl_autoload_register(‘testAutoloader’);

    Of course, the paths where file classes are located must be included previously.

  3. Jess Telford says:

    My example above does a mapping from filename to directory. Without this mapping, the autoloader will not find the file and will fail.

    If all your classes have the same name as their file name, then your shorter method will work perfectly.

    Unfortunately, before namespacing in PHP, it was quite common to name your class differently to the filename.

    For example, ‘class Cookie’ in ‘src/Cookie.php’ will clash with ‘class Cookie’ in ‘src/localstorage/Cookie.php’. So, the classes would then be renamed to ‘class Cookie’ in ‘src/Cookie.php’ and ‘class Localstorage_Cookie’ in ‘src/localstorage/Cookie.php’.

    Your code above would not be able to find and load the Localstorage_Cookie class.

  4. Hi There

    Thanks for this.

    I would like to use your code but can’t see what it is licensed as. Is there a way you could indicate that so that I can use it?

  5. Sobin says:

    Thank you very much for the nice tutorial… It really helped us.

  6. Helge Söderström says:

    Hello Jess, and many thanks for your class! I’ve made a version of my own which mainly introduces two things:

    1. It is fully PSR-0 compliant (i.e. it allows to have a class name where the directory path is separated by underscores instead of namespaces). You should use namespacing, but following a standard is always good.

    2. It introduces the registerNamespace()-method, which will solve the issue that classes with the same name in different namespaces can be properly loaded (with the directory traversal, the class names will overwrite each other).

    You can find it here:
    http://pastebin.com/ZDE4WQ59

    Thanks again!

    PS. Noticed that I kept my own namespace in the class, along with the PHPDoc package and category annotations when I made the pastebin, those can just be removed.

  7. cl3dson says:

    thanks man, this autoload class saved my life,this was the only thins that worked.

Leave a Reply

Your email address will not be published. Required fields are marked *