2015-06-18 6 views
2

У меня проблема с моим тестом PHPUnit на Symfony2. Чтобы подключиться к моему приложению, я использую веб-службу, поэтому создал UserProvider. В моей функции loadUserByUsername Я использую параметры Symfony2, сохраненные в app/config/parameters.yml. Поскольку я не в контроллере нужно использовать глобальную переменную $kernel и получить мои аргументы, как это:Symfony2 & PHPUnit - Access getContainer()

global $kernel; 

$url = $kernel->getContainer()-getParameter('myparam'); 

Когда я использую мое приложение, это работает, но когда я пишу тест, как это:

$crawler = $client->request('GET', '/login'); 
$form = $crawler->selectButton('submit')->form(); 
$form['_username'] = $username; 
$form['_password'] = $pass; 

и выполнить PHPUnit я получаю эту ошибку:

Фатальная ошибка: Вызов функции члена getContainer()

Как я могу получить доступ к параметрам Symfony2 или использовать getContainer при выполнении PHPUnit?

ответ

2

В документации Symfony How to Create a custom UserProvider, в разделе «Создание службы для пользователей провайдера» говорится:

Реальное внедрение провайдера пользователя, возможно, имеют некоторые зависимости или параметры конфигурации или другие услуги. Добавьте их как аргументы в определение службы.

Таким образом, вместо использования глобальной переменной $ kernel вы должны передавать соответствующие параметры в свою службу поставщика услуг, определяя их как аргументы в определении службы. Например:

services: 
    webservice_user_provider: 
     class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider 
     arguments: [%parameter_one%, %parameter_two%, ...] 

Как и с любым сервисом, ваш пользователь класса поставщик услуг должен затем иметь конструктор, который принимает аргументы, соответствующие тем, в определении услуг и хранит их в частных переменных для использования в методах этой службы:

class WebserviceUserProvider implements UserProviderInterface 
{ 
    private parameterOne; 
    ... 

    public function __construct($parameterOne, ...) 
    { 
     $this->parameterOne = $parameterOne; 
     ... 
    } 
    ... 
} 

Я писал приложения Symfony четыре года, и мне никогда не нужно использовать глобальную переменную $ kernel. Я полагаю, что могут быть действительные обстоятельства, но в целом я бы сказал, что этого следует избегать.

+0

благодарственное redbirdo, это работает!я передаю @service_container моей службе, и она передана в PHPUnit – Remi

+0

@Remi, тогда вы должны были принять мой ответ, а не redbirdo's XD. Он вообще не упоминает '@ service_container'. Кстати, как я ответил в комментарии к моему ответу, что это не лучшая практика, так что вам, вероятно, придется немного реорганизовать ваш сервис :) –

+1

@Remi - действительно лучше передать конкретные параметры/услуги вашему сервису, потому что это делает интерфейс/зависимости явные. – redbirdo

1

Прежде всего, вы не должны обращаться к ядру, используя ключевое слово global. Вы всегда можете вводить его как зависимость (то есть @kernel). Тем не менее, если ваша цель - просто получить доступ к контейнеру, тогда вы должны ввести только контейнер обслуживания, который равен @service_container. Правильный и лучший способ - ввести только такие параметры, как предложенный redbirdo (т. Е. %parameter_one%, %parameter_two%).

Что касается вашего теста, вы должны создать и загрузить ядро ​​с двигателем, чтобы иметь возможность использовать его.

Я предлагаю вам создать класс KernelTestCase, чтобы ваш тест мог расширить его и получить доступ к контейнеру и всему, что вы делаете в своих контроллерах.

<?php 

namespace AppBundle\Tests; 

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase as TestCase; 

/** 
* Class KernelTestCase 
* @package AppBundle\Tests 
* @author Francesco Casula <[email protected]> 
*/ 
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); 
    } 
} 

А потом в тестовом классе ...

<?php 

namespace AppBundle\Tests\IntegrationTests; 

use AppBundle\Tests\KernelTestCase as TestCase; 

/** 
* Class ExampleTest 
* @package AppBundle\Tests\IntegrationTests 
* @author Francesco Casula <[email protected]> 
*/ 
class ExampleTest extends TestCase 
{ 
    public function testMyMethod() 
    { 
     $parameter = $this->getParameter('whatever'); 

     // ... 

     $service = $this->getContainer()->get('service'); 
    } 
} 
+0

Я не думаю, что это имеет отношение к проблеме OP. Тестовый фрагмент содержит ссылку на $ client и $ crawler, что означает, что они должны использовать Symfony WebTestCase. WebTestCase загружает ядро ​​для вас, и вы используете $ this-> getClient() -> getContainer() для получения контейнера. Вопрос не задал вопрос о том, как получить контейнер/параметры из тестовой программы, но почему при использовании теста не удалось использовать глобальное ядро ​​$ kernel. – redbirdo

+0

Я ответил, говоря, как получить ядро ​​(а не глобальное), дающее три разных способа сделать это. Для полноты я добавил также способ получить ядро ​​в тестовом примере. Таким образом, ответ уместен, если вы предоставляете дополнительную информацию, которую знаете ... –

+0

Благодарим за помощь, что она работает с arg @service_container в объявлении сервиса! – Remi

Смежные вопросы