Kris Wallsmith

Symfony Guru at opensky.com.
Discussing web development, Symfony and fatherhood.

Posts tagged PHPUnit

Feb 21

Faster PHPUnit

I made a simple optimization to the test suite at OpenSky over the weekend and we are reaping big benefits. The premise is pretty straight forward. We use the setUp() method to create a lot of mock objects through our test suite. If these are allowed to accumulate they end up wasting a lot of space and slowing down your suite.

Luckily there is also a tearDown() method you can use to cleanup, and you can do it automatically if you use this base class:

<?php

abstract class BaseTestCase extends PHPUnit_Framework_TestCase
{
    protected function tearDown()
    {
        foreach ($this->getTearDownProperties() as $prop) {
            $prop->setValue($this, null);
        }

        parent::tearDown();
    }

    /**
     * Returns an array of ReflectionProperty objects for tear down.
     */
    private function getTearDownProperties()
    {
        static $cache = array();

        $class = get_class($this);
        if (!isset($cache[$class])) {
            $cache[$class] = array();
            $refl = new ReflectionClass($class);
            $filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;
            foreach ($refl->getProperties($filter) as $prop) {
                if (0 !== strpos($prop->getDeclaringClass()->getName(), 'PHPUnit_')) {
                    $prop->setAccessible(true);
                    $cache[$class][] = $prop;
                }
            }
        }

        return $cache[$class];
    }
}

Our buddy Jenkins is much happier now — builds are approximately 20% faster.


Oct 29

Oct 18

RFC: Mocking Fluent Interfaces in PHPUnit

I sent a pair of pull requests to Sebastian this morning for a very simple change that will make mocking fluent interfaces much easier.

This is how you might mock a fluent interface using PHPUnit 3.5.1:

$mock = $this->getMock('Person');
$mock
  ->expects($this->any())
  ->method('setName')
  ->will($this->returnValue($mock));

I’ve proposed a new returnSelf() method, which would cleanup this code, just a bit:

$mock = $this->getMock('Person');
$mock
  ->expects($this->any())
  ->method('setName')
  ->will($this->returnSelf());

What do you think?


My last presentation during my month of travel was a tech talk on Symfony2 at the L//P offices in Zürich. I did some live coding and demonstrated how to reference a controller from the DIC and unit test that controller using PHPUnit.

My last presentation during my month of travel was a tech talk on Symfony2 at the L//P offices in Zürich. I did some live coding and demonstrated how to reference a controller from the DIC and unit test that controller using PHPUnit.


Oct 17

How to Test a Symfony2 Bundle

The Symfony2 Framework is fully unit tested using PHPUnit. When you create a Symfony2 bundle to share with the community, it’s important that your bundle also be fully unit tested. It’s also important that users be able to run your bundle’s test suite without having to wrap it in a dummy project. This blog post is about how to set that up.

PHPUnit Configuration

Configuration for PHPUnit should be in a file named phpunit.xml.dist in your bundle’s root directory. This file is suffixed .dist since it is the distributed configuration. Users can copy this default configuration to phpunit.xml and make modifications there for their environment. This is necessary when testing bundles, as we’ll see below.

The distributed configuration file should look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./Tests/bootstrap.php">
  <php>
    <!-- <server name="SYMFONY" value="/path/to/symfony" /> -->
  </php>
  <testsuites>
    <testsuite name="FacebookBundle Test Suite">
      <directory suffix="Test.php">./Tests</directory>
    </testsuite>
  </testsuites>
  <filter>
    <whitelist>
      <directory>./</directory>
      <exclude>
        <directory>./Tests</directory>
      </exclude>
    </whitelist>
  </filter>
</phpunit>

This configuration instructs PHPUnit to include a bootstrap file located at the base of your bundles Tests/ directory before running any tests. We’ll take a look at this file below.

The <php> portion of the configuration will define the $_SERVER['SYMFONY'] variable. After copying this file to phpunit.xml, users will need to uncomment this line and enter the actual path to the Symfony2 src/ directory here.

The <testsuites> section of the file tells PHPUnit where to find your test cases. The <filter> section defines a whitelist of files that the test suite is covering, which excludes the test cases themselves.

Autoloading

The purpose of the bootstrap file referenced in the PHPUnit configuration is to initialize autoloading of classes from the Symfony core and from your bundle. The former can be done using the UniversalClassLoader:

require_once $_SERVER['SYMFONY'].'/Symfony/Component/HttpFoundation/UniversalClassLoader.php';

use Symfony\Component\HttpFoundation\UniversalClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespace('Symfony', $_SERVER['SYMFONY']);
$loader->register();

Notice the use of $_SERVER['SYMFONY'], which we defined earlier in the PHPUnit configuration?

Loading of your bundle’s classes is a bit more complicated since we can’t rely on them be installed in any particular directory, but a hack like this will do the trick:

spl_autoload_register(function($class)
{
    if (0 === strpos($class, 'Bundle\\Kris\\FacebookBundle\\')) {
        $path = implode('/', array_slice(explode('\\', $class), 3)).'.php';
        require_once __DIR__.'/../'.$path;
        return true;
    }
});

With this configuration and bootstrap script in place, running your bundle’s test suite is easy as pie:

$ cd ~/Sites/FacebookBundle
$ phpunit