Testing Symfony2 Commands – Mocking the DI Container with Mockery

Some months ago I had the pleasure to contribute to phpcassa library, maintained by Tyler Hobbs, adding support for Composer and Travis, and fixing some issues with PHP5.4 and some E_STRICT warnings.

And, as there is no Symfony2 bundle for Cassandra yet, I have decided to create one myself. The bundle is at early stages but I have already developed some basic Administration commands, mainly to create a Keyspace, a ColumnFamily, etc… and I have decided to share here how I have unit-tested those commands.

Let’s be honest, PHPUnit is the de-facto standard for Unit Testing, we owe a lot to this amazing library but its mocking framework is a bit cumbersome and limited. And because of that, I’ve been using Mockery for some time and I am pretty happy about it.

So, let’s see how can unit test a Symfony2 Command with the help of Mockery! Let’s forget about pure TDD, we will start showing the Create Keyspace Command and afterwards we will see how to test it.

<?php
 
namespace ADR\Bundle\CassandraBundle\Command;
 
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use phpcassa\SystemManager;
 
class CassandraCreateKeyspaceCommand extends ContainerAwareCommand
{
    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    private $input;
 
    /**
     *
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    private $output;
 
    /**
     * {@inheritDoc}
     */
    protected function configure()
    {
        parent::configure();
        $this
            ->setName('cassandra:keyspace:create')
            ->setDescription('Creates the configured keyspace in selected cluster')
            ->addArgument('client', InputArgument::REQUIRED, 'The client name in Symfony2 where keyspace will be created')
            ->addArgument('keyspace', InputArgument::REQUIRED, 'The keyspace name')
            ->setHelp(<<<EOT
The <info>cassandra:keyspace:create</info> command creates the configured keyspace in the selected cluster.
 
<info>app/console cassandra:keyspace:create [client] [keyspace]</info>
EOT
            );
    }
 
    /**
     * {@inheritDoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->output = $output;
 
        $keyspace = $input->getArgument('keyspace');
 
        $manager = $this->getContainer()->get('cassandra.' . $input->getArgument('client') . '.manager');
        $manager->create_keyspace($keyspace, array());
 
        $output->writeln('<info>Keyspace ' . $keyspace . ' successfully created at ' . $manager->getServer() . '</info>');
    }
}

In this bundle, via its Dependency Injection Extension file, a ‘cassandra.xxx.manager’ service is created (being xxx how we are naming that cassandra cluster) and this service is basically a wrapper for phpcassa\SystemManager class. Details on how to do it can be found here

But how can we unit test this command? Lots of widely used bundles do not test commands and yes, to be honest, testing this does not add big value but it can still work for a small Container mocking example.

So, to unit test the command, we could do something like this:

<?php
 
namespace ADR\Bundle\CassandraBundle\Tests\Command;
 
use Symfony\Component\Console\Application;
use ADR\Bundle\CassandraBundle\Command\CassandraCreateKeyspaceCommand;
use Symfony\Component\Console\Tester\CommandTester;
use Mockery as m;
 
class CassandraCreateKeyspaceCommandTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider getNonInteractiveData
     */
    public function testCreateKeyspaceCommand($input)
    {
        $application = new Application();
        $application->add(new CassandraCreateKeyspaceCommand());
 
        $command = $application->find('cassandra:keyspace:create');
        $command->setContainer($this->getMockContainer($input));
 
        $tester = new CommandTester($command);
        $tester->execute(
            array_merge(array('command' => $command->getName()), $input)
        );
 
        $this->assertEquals('Keyspace ' . $input['keyspace'] . ' successfully created at #mockServer#' . PHP_EOL, $tester->getDisplay());
    }
 
    private function getMockContainer($input)
    {
        $systemManager = m::mock('phpcassa\SystemManager');
        $systemManager
            ->shouldReceive('create_keyspace')
            ->once()
            ->with($input['keyspace'], array())
        ;
 
        $systemManager
            ->shouldReceive('getServer')
            ->once()
            ->withNoArgs()
            ->andReturn('#mockServer#')
        ;
 
        $container = m::mock('Symfony\Component\DependencyInjection\Container');
        $container
            ->shouldReceive('get')
            ->once()
            ->with('cassandra.' . $input['client'] . '.manager')
            ->andReturn($systemManager)
        ;
 
        return $container;
    }
 
    public function getNonInteractiveData()
    {
        return array(
            array(array('keyspace' => 'fooKeySpace', 'client' => 'barClient')),
        );
    }
}

The most interesting part is that we create a mocked phpcassa\SystemManager and a mocked Symfony\Component\DependencyInjection\Container with the calls and responses we expect from them.

As you can see, Mockery’s fluent interface seems to be more clear and intuitive than PHPUnit and makes it easier to test Symfony2 commands!

I hope you’ve liked this example and I promise to blog more often than these latest months!

You may also like...