PHPUnit & Pimple: Integration Tests with a Simple DI Container

Unit tests are tests on a micro-scale. When unit testing, you are testing little units of your code to make sure that, given a certain input, they produce the output you expected. When your unit of code makes calls to other objects, you can “mock” or “stub” these objects and verify that a method is called a specific number of times, or to make sure the unit of code you’re testing will receive the correct data from the other objects.

But when you have a nice landscape of classes, which are all very well tested, you have to move up a bit on the testing pyramid and test some of these classes when they are working together. This usually requires some setup. Say you have some class, called a BatchProcessor, that works directly with a Doctrine\DBAL\Connection object. You may have a nice set of unit tests for it, to verify that the right queries are executed. But you also want to have a test which really integrates your class with Doctrine DBAL and with a real database (even though it would be an in-memory SQLite database). The best way to accomplish this is to create and configure the Connection class in the setUp() method of your test class:

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\Schema;

class BatchProcessorIntegrationTest extends \PHPUnit_Framework_TestCase
{
    private $connection;
    private $batchProcessor;

    protected function setUp()
    {
        $this->connection = DriverManager::getConnection(
            array(
                'driver' => 'pdo_sqlite',
                'memory' => true,
            )
        );

        $schema = new Schema();

        $table = $schema->createTable('users);
        $table->addColumn('name', 'string');
        ...

        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
            $this->connection->exec($sql);
        }

        $this->batchProcessor = new BatchUpdater($this->connection);
    }

    public function testProcessSomething()
    {
        $this->batchProcessor->...;
    }

    protected function tearDown()
    {
        $this->connection->close();

        // to help the garbage collector:
        $this->connection = null;
        $this->batchProcessor = null;
    }
}

With a test like this, you can assure yourself that things will really work, once your code is integrated with Doctrine DBAL. However, soon you will create a second test class in which you would be happy to use the same code for setting up a connection and a schema. You could choose to create some kind of an abstract test class, but then each test class is required to extend from this base class. It would be cleaner (and better maintainable) if you could reuse creation logic required by your test class by having some kind of an external service container from which you could fetch whatever object you needed. Let’s first set up some classes and interfaces for the desired functionality and then continue with some more advanced examples.

A simple service container

Reusing creation logic almost immediately brings my mind to the concept of a dependency injection or service container. It need not be very advanced though, since it will just execute from within a unit test. I chose Pimple for this, since it is very well suited for the purpose: defining an entire object graph with dependencies, support for extending services and for keeping plain parameters.

We will have a generic service container, extending the Pimple dependency injection container, and service providers, which can be used to register services on the fly. These are their interfaces:

interface ServiceContainerInterface
{
    public function registerProvider(ServiceProviderInterface $serviceProvider);
}

interface ServiceProviderInterface
{
    public function register(\Pimple $container);
}

A very basic implementation of a service container would be:

class ServiceContainer extends \Pimple implements ServiceContainerInterface
{
    public function registerProvider(ServiceProviderInterface $provider)
    {
        $provider->register($this);
    }
}

Service providers

Now each service provider is allowed to register services and parameters using the container. For example this would be the ConnectionProvider:

use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Schema;

class ConnectionProvider implements ServiceProviderInterface
{
    public function register(\Pimple $container)
    {
        // set parameters for creating the connection
        $container['connection_options'] = array(
            'driver' => 'pdo_sqlite',
            'memory' => true,
        );

        // define the "connection" service
        $container['connection'] = $container->share(
            function(\Pimple $container) {
                return DriverManager::getConnection(
                    $container['connection_options']
                );
            }
        );

        // extend the "connection" service to also load the schema, when defined
        $container['connection'] = $container->extend('connection',
            function(Connection $connection, \Pimple $container) {
                if (!isset($container['schema'])) {
                    return $connection;
                }

                $schema = $container['schema'];
                $databasePlatform = $connection->getDatabasePlatform();

                foreach ($schema->toSql($databasePlatform) as $sql) {
                    $connection->exec($sql);
                }

                return $connection;
            }
        );
    }
}

We also need a BatchProcessorProvider to create an instance of BatchProcessor using the connection service:

class BatchProcessorProvider implements ServiceProviderInterface
{
    public function register(\Pimple $container)
    {
        $container['batch_processor'] = $container->share(
            function(\Pimple $container) {
                new BatchProcessor($container['connection');
            }
        );
    }
}

A test case which uses the service container

With these service providers and the service container itself in place, we can have the following clean setup in our test class:

class BatchProcessorIntegrationTest extends \PHPUnit_Framework_TestCase
{
    private $container;

    protected function setUp()
    {
        $this->container = new ServiceContainer();
        $this->container->registerProvider(new ConnectionProvider());
        $this->container->registerProvider(new BatchProcessorProvider());

        $schema = new Schema();
        $table = $schema->createTable('users);
        $table->addColumn('name', 'string');
        ...

        $this->container['schema'] = $schema;
    }

    public function testProcessSomething()
    {
        $this->container['batch_processor']->...;
    }

    protected function tearDown()
    {
        $this->container['connection']->close();

        $this->container = null;
    }
}

A terminable service container

It may be well worth it to make the implementation a bit more transparent even. The first step would be to give both the service container and the service providers a method terminate() which can be called to clean things up.

interface ServiceContainerInterface
{
    public function registerProvider(ServiceProviderInterface $serviceProvider);

    public function terminate();
}

interface ServiceProviderInterface
{
    public function register(\Pimple $container);

    public function terminate(\Pimple $container);
}

Then inside the ServiceContainer we keep track of the available service providers and terminate them one by one:

class ServiceContainer extends \Pimple implements ServiceContainerInterface
{
    private $providers = array();

    public function registerProvider(ServiceProviderInterface $provider)
    {
        $this->providers[] = $provider;
        $provider->register($this);
    }

    public function terminate()
    {
        foreach ($this->providers as $provider) {
            $provider->terminate($this);
        }
    }
}

The ConnectionProvider uses its terminate() method just to close the connection:

class ConnectionProvider
{
    ...

    public function terminate(\Pimple $container)
    {
        $container['connection']->close();
    }
}

The tearDown() method in the test class now becomes:

protected function tearDown()
{
    $this->container->terminate();

    $this->container = null;
}

Required arguments for service providers

As we saw above, the ConnectionProvider defines a connection service. This service is then extended to make sure a given Schema is transformed to SQL statements which are then executed. Since this may be something you always want to be taken care of, you could say that providing a schema service, which is in fact a Schema object, is mandatory when you want to use the ConnectionProvider. This requires the following changes to the ConnectionProvider class:

class ConnectionProvider implements ServiceProviderInterface
{
    private $schema;

    public function __construct(Schema $schema)
    {
        $this->schema = $schema;
    }

    public function register(\Pimple $container)
    {
        $container['schema'] = $this->schema;
    }
}

Then the setUp() method of your test class becomes:

protected function setUp()
{
    $schema = new Schema();
    $table = $schema->createTable('users);
    $table->addColumn('name', 'string');
    ...

    $this->container = new ServiceContainer();
    $this->container->registerProvider(new ConnectionProvider($schema));
    $this->container->registerProvider(new BatchProcessorProvider());
}

Continuing this idea…

I have been using this method of decoupling the creation of object graphs from unit tests and it’s been very helpful in keeping test classes clean and making creation logic very portable and thereby reusable.

There are of course many more situations you could think of when using a simple dependency injection container like Pimple helps keeping your mind straight.

Posted in Dependency injection container, PHPUnit, Testing | 2 Comments

Slides for my “Dependency Injection Smells” talk

Below you will find the slides for my presentation about Dependency Injection Smells (see also one of my previous posts from which this idea originated). If you have any questions, or feedback, please let me know by posting a comment below (or on joind.in).

Posted in Conferences, Dependency injection container | Leave a comment

Dependency Injection Smells – Speaking at the Dutch PHP Conference

This week is Dutch PHP Conference week! It is something I’m very excited about. Three years ago I attended this conference for the first time. It was also my first
time to visit any (PHP) conference at all, and this is a big conference, with lots of speakers, lots of attendees and multiple tracks. The Dutch PHP Conference (DPC) has a certain grandeur – many large rooms, many famous developers speaking. It is an inspiring event. Visiting the conference back in 2010 lit a fire in me to start
investigating anything there was to know about writing good code: reusable, clean code, with well named, short and testable methods, readable and maintainable by others. In other words, I started being critical of the code I produced.

It’s quite interesting for me to read a blog post I wrote back then on the Driebit Symfony blog. Many things have changed: I have my own blog about Symfony2, acquired some certificates, am writing a book about Symfony2, and have started as a freelance consultant. Not to mention that I’ve bought a house and formed a family!

Now, it’s already very exciting to be an attendee, but this year I will also be speaking at the DPC about “Dependency Injection Smells”. The idea for this talk originated from an earlier post with the same title. When reading some code in search of them I came to the conclusion that there are a lot of them! These are the smells that I will discuss this Friday:

  • Static dependency
  • Missing dependency auto-recovery
  • Hidden dependencies
  • Creation logic reduction
  • Factory methods
  • Programming against an implementation
  • Dependencies prohibited

And even this long list is not exhaustive. So, if you’re coming to the conference, listen to my talk. Think about how you should design your classes, and how a class should handle its dependencies. Then afterwards introduce yourself, and tell me what you think.

P.S. Did you notice: this is a post with no code in it!

Posted in Conferences | Leave a comment

Symfony2: Defining and Dispatching Custom Form Events

The Symfony Form Component has an architecture that keeps inspiring me. It supports horizontal and vertical inheritance and it has a solid event system on a very fine-grained level. In this post I would like to explain how you can expand the event system with your own events. As an example., I will create a new event type used for indicating that the bound form is valid.

Using event listeners and event subscribers with forms

As you can read in a Symfony Cookbook article you can already hook into special form events, defined in Symfony\Component\Form\FormEvents:

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;

class CustomFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
            // respond to the event, modify data, or form elements

            $data = $event->getData();
            $form = $event->getForm();
        });
    }
}

When the set of events you want to listen to does not have to be determined dynamically, you could also use an event subscriber instead of an event listener:

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CustomSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData'
        );
    }

    public function preSetData(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        // ...
    }
}

Registering an event subscriber goes like this:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventSubscriber(new CustomSubscriber());
}

Every form element has its own dispatcher

The nice thing is: event listeners and event subscribers work on the level of a form element, which could be the main form object, or any specific form field in the tree of form elements. This allows you to be very specific in your actions inside the listener itself.

Each form element has its own event dispatcher, due to this line in ResolvedFormType::createBuilder():

$builder = new FormBuilder($name, $dataClass, new EventDispatcher(), $factory, $options);

This builder will be passed around as the argument for the buildForm() methods. And every call to addEventSubscriber() or addEventListener() will result in an extra event subscriber or listener on this little event dispatcher object.

After a form has been fully resolved, you can still retrieve its event dispatcher:

$eventDispatcher = $form->getConfig()->getEventDispatcher();

Firing custom events on form elements

With the event dispatcher at our service, we can now fire any event we like on form elements. As I mentioned, I want to be able to notify event subscribers of the fact that the bound form has been validated. This means I need an event class and an event name to dispatch. The event class could be:

use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Form\FormInterface;

class ValidFormEvent extends Event
{
    private $form;

    public function __construct(FormInterface $form)
    {
        $this->form = $form;
    }

    public function getForm()
    {
        return $this->form;
    }
}

I choose the event name valid_form and then inside a controller we can do:

if ($form->isValid()) {
    $eventDispatcher = $form->getConfig()->getEventDispatcher();
    $eventDispatcher->dispatch('valid_form', new ValidFormEvent($form));
}

This will have no effect whatsoever, until we register a listener in a form type:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventListener('valid_form', function(FormEvent $event) {
        // ...
    }
}

Propagating custom events

As I already mentioned, each form element has its own event dispatcher. This means that the dispatch() call issued in the code above will not reach any child form elements and therefore may still have no effect, only for the root form element.

There is a simple trick for this – use an event subscriber which merely propagates the custom form event:

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormInterface;

class PropagateValidFormEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'form_valid' => 'onFormValid',
        );
    }

    public function onFormValid(FormValidEvent $event)
    {
        $form = $event->getForm();

        foreach ($form as $child) {
            /** @var $child FormInterface */

            $childEventDispatcher = $child->getConfig()->getEventDispatcher();
            $childEventDispatcher->dispatch('form_valid', new FormValidEvent($child));
        }
    }
}

We simple recreate the event for child elements and dispatch it using the event dispatcher of the child element.

Now we only have to make sure that every form element has this event subscriber by default, since we don’t want to (and can’t) call

$builder->addEventSubscriber(new PropagateValidFormEventSubscriber());

in all buildForm() methods.

Creating a form type extension for registering event subscribers

Luckily, the Form Component has support for form type extensions, which means that we can modify a form type’s behavior at all different levels of abstraction (this is the “horizontal inheritance” part), without requiring other form types to have a different parent form type. A form type extension looks very much like a normal form type, except that its buildForm() method will be called for all form elements whose type is (either directly or in the hierarchy of parent types) the type returned by getExtendedType(). This allows us to add the omnipresent PropagateValidFormEventSubscriber to the event dispatcher of each and every form.

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;

class ValidFormEventTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventSubscriber(new PropagateValidFormEventSubscriber());
    }

    public function getExtendedType()
    {
        return 'form';
    }
}

To make the type extension work, register this class as a service with a tag form.type_extension and an alias attribute with the value “form”.

From now on, you can happily dispatch the form_valid event on the root form element’s event dispatcher, and it will be automatically propagated to all child form elements.

Posted in Events, Forms, Symfony2 | 12 Comments