当我们开始在几个项目中使用 Symfony 2 时,我们发现有相当多的代码可以在我们的项目之间共享。因此,我们开始将功能提取到 Symfony 2 包中,以便在我们的项目之间共享它们。
虽然我们基本上一切正常,但仍有很多问题不容易通过谷歌搜索到,特别是在测试共享包时。
我们提取的第一个小包包含一个 Doctrine Entity、一个自动注入到客户端项目的 DI 容器中的
kernel.event_listener
、一个注释、另一个服务和几个命令。基本思想是客户端项目可以使用我们的注释来注释其控制器,event_listener 将拦截对带注释的控制器的请求并在最终调用控制器之前执行一些附加逻辑(涉及条令实体)。这些命令旨在管理学说实体的数据库条目。
到目前为止,一切都按照我们的预期运行,但我们正在努力解决捆绑包的可测试性问题。首先,保存该包的 Git 存储库不包含完整的 Symfony2 项目。这太过分了,因为我们只是在这里构建一个捆绑包,而不是整个应用程序,对吧?
但是我们如何测试事件监听器呢?我们如何测试它是否被注入到 DI 容器中?我们需要一个测试控制器,该控制器将用我们的特殊注释进行注释,以便我们可以测试我们的事件侦听器是否正确捕获它。该控制器只能在测试时可用,并且绝不能出现在任何客户端应用程序中。
我们如何测试命令?我们需要模拟学说背后的数据库。当我们尝试在简单地使用
/vendor/autoload.php
引导的 phpunit 测试中执行命令时,我们当然会得到:
致命错误:调用未定义的方法 Symfony\Component\Console\Application::getKernel() 中 /.../vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php 3号线
所以感觉我们最终将需要在我们的捆绑包存储库中使用整个 Symfony2 项目,以便引导整个框架,以便最终能够测试我们的组件。当我查看开源 Symfony2 捆绑包时,我发现没有一个捆绑包将整个框架签入到他们的 Git 存储库中,所以这仍然感觉不对。
我错过了什么?我是否缺少有关仅捆绑包/无应用程序捆绑包开发的文档?
我在这里找到了命令测试的解决方案:http://www.ricardclau.com/2013/02/testing-symfony2-commands-mocking-the-di-container-with-mockery/
原来错误来自于
ContainerAwareCommand
尝试创建一个新容器,这显然在裸测试环境中无法工作。我通过模拟容器并将其手动注入命令中解决了问题,如下所示:
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;
}
}
我想控制器测试必须以类似的、大量模拟的方式进行。当我找到解决方案时,我会在这里发布完整的答案......
这个问题很老了,但我只是偶然发现的。
捆绑包是 Symfony 共享整个库(包括库)的特定方式。它们各自的依赖注入容器配置。
因此,捆绑包依赖于 Symfony 内核,如果一个捆绑包依赖于另一个捆绑包,则会创建严重的依赖关系,这会有效地阻止单元测试:要测试捆绑包 A 中的单元(类),您需要捆绑包 B 加上 Symfony核心。
您仍然可以进行测试,但生成的测试是验收/集成测试,而不是单元测试。正如您已经注意到的那样,这些不适合测试驱动开发,缓慢且脆弱。
更新
我刚刚写了一篇关于此的博客文章: https://lastzero.net/2015/11/dependent-symfony-2-bundles-and-testability/