As we start using Symfony 2 on several projects now, we find that there is quite a bit of code we could share among our projects. So we started extracting features into Symfony 2 bundles in order to share them between our projects.
While we basically got things working, there are quite a few questions remaining that aren't easily googleable, especially when it comes to testing the shared bundle.
The first small bundle we extracted contains a Doctrine Entity, a kernel.event_listener
that's being auto-injected into the client project's DI container, an annotation, another service and a couple of commands. The basic idea is that the client project can annotate its controllers with our annotation, the event_listener will intercept requests to the annotated controllers and execute some additional logic (involving the doctrine entity) before the controller is eventually invoked. The commands are intended to administer database entries of the doctrine entity.
So far, everything works exactly as we expected, but we're struggling with the testability of the bundle alone. First off, the Git repository that holds the bundle doesn't contain a complete Symfony2 project. That would be overkill as we're only building a bundle here, not a whole application, right?
But how can we test the event listener? How can we test it's being injected into the DI container? We'd need a test Controller that's gonna be annotated with our special annotation, so we can test that our event listener captures it correctly. That controller must only ever be available when testing and must never show up in any client application.
How can we test the Commands? We'd need to mock the database behind doctrine. When we try to execute the command in a phpunit test that's simply bootstrapped with /vendor/autoload.php
, of course we get:
Fatal error: Call to undefined method Symfony\Component\Console\Application::getKernel() in /.../vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php on line 3
So it feels like we're gonna end up needing a whole Symfony2 project in our bundle's repository anyway in order to bootstrap the whole framework to be able to eventually test our components. When I looked at open source Symfony2 bundles, I found none that had the whole Framework checked into their Git repos, so that still feels wrong.
What am I missing? Is there a piece of documentation about Bundle-Only / Applicationless bundle development I am missing?
Edit:
I found a solution for command testing here: http://www.ricardclau.com/2013/02/testing-symfony2-commands-mocking-the-di-container-with-mockery/
It turned out the error came from ContainerAwareCommand
trying to create a new container, which obviously wouldn't work in a bare test environment. I solved the problem by mocking the container and injecting it into the Command manually like so:
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
class MyCommandTest extends \PHPUnit_Framework_TestCase {
public function testExecute() {
$application = new Application();
$application->add(new MyCommand());
$command = $application->find('my:command');
$command->setContainer($this->getMockContainer()); // <= This avoids ContainerAwareCommand creating a 'real' container in a test env
$commandTester = new CommandTester($command);
$commandTester->execute(array('command' => $command->getName()));
print $commandTester->getDisplay();
$this->assertRegExp('/.../', $commandTester->getDisplay());
}
protected function getMockContainer() {
// Mock the container and everything you'll need here
$mockDoctrine = $this->getMock('Symfony\Bridge\Doctrine\RegistryInterface');
$mockDoctrine->...;
$mockContainer = $this->getMock('Symfony\Component\DependencyInjection\Container');
$mockContainer->expects($this->once())
->method('get')
->with('doctrine')
->willReturn($mockDoctrine);
return $mockContainer;
}
}
I guess the Controller testing will have to work in a similar, mock-heavy, way. When I find a solution for it, I'll post a complete answer here...