Symfony2 和 PHPUnit - 访问 getContainer()
Symfony2 & PHPUnit - Access getContainer()
我在 Symfony2 上的 PHPUnit 测试有问题。为了连接到我的应用程序,我使用了 Web 服务,因此我创建了一个 UserProvider。在我的函数 loadUserByUsername
中,我使用保存在 app/config/parameters.yml
中的 Symfony2 参数。因为我不在控制器中,所以我需要使用全局变量 $kernel
并像这样获取我的参数:
global $kernel;
$url = $kernel->getContainer()-getParameter('myparam');
当我使用我的应用程序时,它可以工作,但是当我这样编写测试时:
$crawler = $client->request('GET', '/login');
$form = $crawler->selectButton('submit')->form();
$form['_username'] = $username;
$form['_password'] = $pass;
并执行 PHPUnit 我得到这个错误:
Fatal error : Call to a member function getContainer()
如何在执行 PHPUnit 时访问 Symfony2 参数或使用 getContainer
?
在 Symfony 文档 How to Create a custom UserProvider 下,'Create a Service for the User Provider' 指出:
The real implementation of the user provider will probably have some
dependencies or configuration options or other services. Add these as
arguments in the service definition.
因此,与其使用全局 $kernel 变量,不如将相关参数定义为服务定义中的参数,从而将它们传递到用户提供程序服务中。例如:
services:
webservice_user_provider:
class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider
arguments: [%parameter_one%, %parameter_two%, ...]
与任何服务一样,您的用户提供者服务 class 必须有一个构造函数,该构造函数接受与服务定义中的参数相对应的参数,并将它们存储在私有变量中以供在服务的方法中使用:
class WebserviceUserProvider implements UserProviderInterface
{
private parameterOne;
...
public function __construct($parameterOne, ...)
{
$this->parameterOne = $parameterOne;
...
}
...
}
我编写 Symfony 应用程序已经四年了,我从来不需要使用全局 $kernel 变量。我敢说可能存在有效的情况,但总的来说我会说这是应该避免的。
首先你不应该使用global
关键字访问内核。您始终可以将其作为依赖项注入(即 @kernel
)。不过,如果您的目标只是访问容器,那么您应该只注入 @service_container
的服务容器。正确和最好的方法是像 redbirdo 建议的那样注入参数(即 %parameter_one%
、%parameter_two%
)。
关于您的测试,您应该创建并启动一个 propel 内核以便能够使用它。
我建议您创建一个 KernelTestCase
class 来执行此操作,以便您的测试可以扩展它并访问容器以及您在控制器中所做的一切。
<?php
namespace AppBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase as TestCase;
/**
* Class KernelTestCase
* @package AppBundle\Tests
* @author Francesco Casula <fra.casula@gmail.com>
*/
abstract class KernelTestCase extends TestCase
{
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
/**
* {@inheritdoc}
*/
public function tearDown()
{
$this->container = null;
parent::tearDown();
}
/**
* @param array $options
* @return \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected function getContainer(array $options = [])
{
if (!$this->container) {
static::bootKernel($options);
$this->container = static::$kernel->getContainer();
}
return $this->container;
}
/**
* @param string $parameter
* @return mixed
*/
protected function getParameter($parameter)
{
return $this->getContainer()->getParameter($parameter);
}
}
然后在你的测试中class...
<?php
namespace AppBundle\Tests\IntegrationTests;
use AppBundle\Tests\KernelTestCase as TestCase;
/**
* Class ExampleTest
* @package AppBundle\Tests\IntegrationTests
* @author Francesco Casula <fra.casula@gmail.com>
*/
class ExampleTest extends TestCase
{
public function testMyMethod()
{
$parameter = $this->getParameter('whatever');
// ...
$service = $this->getContainer()->get('service');
}
}
我在 Symfony2 上的 PHPUnit 测试有问题。为了连接到我的应用程序,我使用了 Web 服务,因此我创建了一个 UserProvider。在我的函数 loadUserByUsername
中,我使用保存在 app/config/parameters.yml
中的 Symfony2 参数。因为我不在控制器中,所以我需要使用全局变量 $kernel
并像这样获取我的参数:
global $kernel;
$url = $kernel->getContainer()-getParameter('myparam');
当我使用我的应用程序时,它可以工作,但是当我这样编写测试时:
$crawler = $client->request('GET', '/login');
$form = $crawler->selectButton('submit')->form();
$form['_username'] = $username;
$form['_password'] = $pass;
并执行 PHPUnit 我得到这个错误:
Fatal error : Call to a member function getContainer()
如何在执行 PHPUnit 时访问 Symfony2 参数或使用 getContainer
?
在 Symfony 文档 How to Create a custom UserProvider 下,'Create a Service for the User Provider' 指出:
The real implementation of the user provider will probably have some dependencies or configuration options or other services. Add these as arguments in the service definition.
因此,与其使用全局 $kernel 变量,不如将相关参数定义为服务定义中的参数,从而将它们传递到用户提供程序服务中。例如:
services:
webservice_user_provider:
class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider
arguments: [%parameter_one%, %parameter_two%, ...]
与任何服务一样,您的用户提供者服务 class 必须有一个构造函数,该构造函数接受与服务定义中的参数相对应的参数,并将它们存储在私有变量中以供在服务的方法中使用:
class WebserviceUserProvider implements UserProviderInterface
{
private parameterOne;
...
public function __construct($parameterOne, ...)
{
$this->parameterOne = $parameterOne;
...
}
...
}
我编写 Symfony 应用程序已经四年了,我从来不需要使用全局 $kernel 变量。我敢说可能存在有效的情况,但总的来说我会说这是应该避免的。
首先你不应该使用global
关键字访问内核。您始终可以将其作为依赖项注入(即 @kernel
)。不过,如果您的目标只是访问容器,那么您应该只注入 @service_container
的服务容器。正确和最好的方法是像 redbirdo 建议的那样注入参数(即 %parameter_one%
、%parameter_two%
)。
关于您的测试,您应该创建并启动一个 propel 内核以便能够使用它。
我建议您创建一个 KernelTestCase
class 来执行此操作,以便您的测试可以扩展它并访问容器以及您在控制器中所做的一切。
<?php
namespace AppBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase as TestCase;
/**
* Class KernelTestCase
* @package AppBundle\Tests
* @author Francesco Casula <fra.casula@gmail.com>
*/
abstract class KernelTestCase extends TestCase
{
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
/**
* {@inheritdoc}
*/
public function tearDown()
{
$this->container = null;
parent::tearDown();
}
/**
* @param array $options
* @return \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected function getContainer(array $options = [])
{
if (!$this->container) {
static::bootKernel($options);
$this->container = static::$kernel->getContainer();
}
return $this->container;
}
/**
* @param string $parameter
* @return mixed
*/
protected function getParameter($parameter)
{
return $this->getContainer()->getParameter($parameter);
}
}
然后在你的测试中class...
<?php
namespace AppBundle\Tests\IntegrationTests;
use AppBundle\Tests\KernelTestCase as TestCase;
/**
* Class ExampleTest
* @package AppBundle\Tests\IntegrationTests
* @author Francesco Casula <fra.casula@gmail.com>
*/
class ExampleTest extends TestCase
{
public function testMyMethod()
{
$parameter = $this->getParameter('whatever');
// ...
$service = $this->getContainer()->get('service');
}
}