Функциональные тесты Symfony 3.4

#php #symfony #phpunit #functional-testing

#php #symfony #phpunit #функциональное тестирование

Вопрос:

Я некоторое время зависал из-за ошибки при выполнении своих функциональных тестов с помощью symfony 3.4.

Мое приложение запускает пользовательский GuardAuthenticator для аутентификации моих пользователей в рамках аутентификации CAS. Это просто для объяснения контекста. В своих тестах я не хочу его использовать, я хочу использовать определенную систему аутентификации.

Я написал функциональные тесты. Я начал свою тестовую среду со следующего :

 # app/config/config_test.yml
imports:
    - { resource: config_dev.yml }

framework:
    test: ~
    session:
        storage_id: session.storage.mock_file
        name: MOCKSESSION
    profiler:
        collect: false
  

Согласно официальному документу Symfony, я расширяю symfony WebTestCase для установки определенного токена аутентификации для моих тестов Как имитировать HTTP-аутентификацию в функциональном тесте

 namespace TestAppBunbleWebTestCase;

use SymfonyBundleFrameworkBundleTestWebTestCase as SfTestCase;

class WebTestCase extends SfTestCase
{
    /**
     *
     * {@inheritDoc}
     * @see PHPUnit_Framework_TestCase::setUp()
     */
    public function setUp()
    {
        $this->client = static::createClient();
        $this->container = $this->client->getContainer();
        $this->entityManager = $this->container->get('doctrine.orm.entity_manager');

        parent::setUp();
    }

    public function logInAs($username)
    {
        $user = $this->entityManager->getRepository(User::class)->loadUserByUsername($username);
        
        $session = $this->client->getContainer()->get('session');
        $token = new PostAuthenticationGuardToken($user, 'main', $user->getRoles());
        $session->set('_security_main', serialize($token));
        
        $session->save();
        
        $cookie = new Cookie($session->getName(), $session->getId());
        $this->client->getCookieJar()->set($cookie);
    }
}

  

Наконец, я написал свой (простой) тест :

 namespace TestsAppBundleController;

use TestsAppBundleWebTestCase;

class DefaultControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $this->logInAs('admin');
        $crawler = $this->client->request('GET', '/');
        $this->assertTrue($client->getResponse()->isSuccessful(), 'response status is 2xx');
    }
}
  

Я запустил phpunit :

 $ ./vendor/bin/simple-phpunit 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

Testing app Test Suite
<br />
<b>Error</b>: <font color="FF0000"><b>Internal script failure</b><br />
  

Для отладки я изменил свой тест :

     public function testIndex()
    {
        $this->logInAs('admin');
        //$crawler = $this->client->request('GET', '/');
        //$this->assertTrue($client->getResponse()->isSuccessful(), 'response status is 2xx');
    }
  

I ran phpunit :

  ./vendor/bin/simple-phpunit 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

Testing app Test Suite
.                                                                   1 / 1 (100%)

Time: 797 ms, Memory: 27.75MB

OK (1 test, 0 assertions)
  

After lots of tests I found when I call in my test :

 $this->client->request('GET', '/')
  

It crash.

UPDATE : another test.

If I ran this :

     public function testIndex()
    {
        $this->logInAs('admin');
        $token = $this->client->getContainer()->get('security.token_storage');
        var_dump($token->getToken());

        //$crawler = $this->client->request('GET', '/');
        //$this->assertTrue($client->getResponse()->isSuccessful(), 'response status is 2xx');
    }
  

Он возвращает меня NULL … нет токена.

ОБНОВЛЕНИЕ 2

Я углубился в код, чтобы вернуться к точке, где symfony останавливается. Это в doDispatch методе SymfonyComponentEventDispatcherEventDispatcher класса.

Это запускает слушателей по событию `kernel.event’. И когда он запускает прослушиватель с именем «Symfony Bundle SecurityBundle Debug TraceableFirewallListener» => «сбой внутреннего скрипта».

ОБНОВЛЕНИЕ 3 Если я сравниваю dev.log (который представляет журнал при запуске приложения в браузере), у меня есть этот журнал по запросу :

 [2020-10-23 13:55:56] request.INFO: Matched route "app_homepage". {"route":"app_homepage","route_parameters":{"_controller":"AppBundle\Controller\DefaultController::indexAction","_route":"app_homepage"},"request_uri":"http://localhost:8180/","method":"GET"} []
[2020-10-23 13:55:56] security.DEBUG: Read existing security token from the session. {"key":"_security_main","token_class":"Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken"} []
[2020-10-23 13:55:56] doctrine.DEBUG: SELECT [...]
[2020-10-23 13:55:56] security.DEBUG: User was reloaded from a user provider.  [...]
  

Но когда я запускаю $this->client->request('GET', '/') , мой test.log показывает :

 [2020-10-23 13:55:56] request.INFO: Matched route "app_homepage". {"route":"app_homepage","route_parameters":{"_controller":"AppBundle\Controller\DefaultController::indexAction","_route":"app_homepage"},"request_uri":"http://localhost","method":"GET"} []
  

Никаких следов от безопасности.

ОБНОВЛЕНИЕ 4: еще один тест

Я провел этот тест :

     public function testIndex()
    {
        $crawler = $this->client->request('GET', '/stackTest');
        $this->assertTrue($this->client->getResponse()->isSuccessful(), 'response status is 2xx');
    }
  

Это страница с такой конфигурацией в security.yml

 firewalls:
    stack:
        pattern: ^/stackTest
        security: false
  

… и тест работает!

  ./vendor/bin/simple-phpunit 
PHPUnit 5.7.27 by Sebastian Bergmann and contributors.

Testing app Test Suite
.                                                                   1 / 1 (100%)

Time: 1.08 seconds, Memory: 36.50MB

OK (1 test, 1 assertion)
  

Комментарии:

1. Может быть, вам нужно реализовать интерфейсы Serializable и EquatableInterface, как указано в этой части документации? symfony.com/doc/current/security /…

2. @Vyctorya, моя пользовательская сущность реализует сериализуемый интерфейс. Когда я смотрю test.log, единственная строка, которую я получаю при вызове $this->client->request('GET', '/') , это [2020-10-23 13:21:01] request.INFO: Matched route "app_homepage". {"route":"app_homepage","route_parameters":{"_controller":"AppBundle\Controller\DefaultController::indexAction","_route":"app_homepage"},"request_uri":"http://localhost/","method":"GET"} [] . Странно, как будто symfony где-то останавливается, отсюда и сообщение «internal servererror».

3. По мере реализации Serializable может случиться так, что не все необходимые свойства сериализуются / не сериализуются должным образом, которые использовались для сравнения, если пользователь изменился. Попробуйте выполнить отладку SymfonyComponentSecurityCoreAuthenticationTokenAbstractToken::hasUserChanged() . Но странно, что он завершается ошибкой, я думаю, проблема где-то в другом месте.

4. @vstelmakh, метод hasUserChanged не достигнут. Я думаю, что «сбой внутреннего скрипта» останавливает процесс раньше. Может быть, я делаю что-то не так для аутентификации для функциональных тестов? Я потерялся… Я думаю, что проблема где-то в другом месте

Ответ №1:

Я нашел !

Как было сказано выше, аутентификация моего приложения выполняется через CAS. Для тестирования я хочу имитировать эту аутентификацию. Итак, я создал FakeGuardAuthenticator, который вместо того, чтобы запрашивать сервер CAS, имитирует его и возвращает учетные данные.

Поэтому в моем файле конфигурации config_test.yml я должен переопределить guardAuthenticator, передав через псевдоним.

Которые дают :

 # app/config/config_test.yml
imports:
    - { resource: config_dev.yml }
    - { resource: services_test.yml }
  
 # app/config/service_test.yml
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    app_cas.guard_authenticator:
        class : TestsAppBundleFakeGuardAuthenticator
        arguments:
          # [...]
        provider: cas
  

Спасибо @vstelmakh и @Vyctorya за то, что нашли время ответить мне.

Комментарии:

1. Приятно знать, что вы нашли решение. Не забудьте принять свой собственный ответ. Это может помочь другим узнать, что проблема решена. stackoverflow.blog/2009/01/06/ принимайте-свои-собственные-ответы