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');
    }
}