Skip to content
Advertisement

Symfony 3.4 Functional tests

I have been stuck for some time on an error to do my functional tests with symfony 3.4.

My app run a custom GuardAuthenticator for authenticate my users within CAS authentication. This is just for explain the context. In my tests, I don’t want to use it, I want to use a specific authentication system.

I wrote functional tests. I started my test environment with the following :

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

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

According to the official Symfony doc, I extends symfony WebTestCase for set a specific authentication token for my tests How to Simulate HTTP Authentication in a Functional Test

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

Finally, I wrote my (simple) test :

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

I ran 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 />

For debugging, I modified my test :

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

It return me NULL… no token.

UPDATE 2

I dove into the code to go back to the point where symfony stops. It’s in the doDispatch method of SymfonyComponentEventDispatcherEventDispatcher class.

It trigger the listeners on the `kernel.event’ event. And when it trigger the listener named “SymfonyBundleSecurityBundleDebugTraceableFirewallListener” => “internal script failure”.

UPDATE 3 If I compare dev.log (which represents log when I run app in browser), I have this log on a request :

[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.  [...]

But when I run $this->client->request('GET', '/'), my test.log show :

[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"} []

No trace from security.

UPDATE 4 : another test

I ran this test :

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

It’s a page with this configuration in security.yml

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

… and the test works!

 ./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)

Advertisement

Answer

I found !

As said above, the authentication of my app is done via CAS. For testing, I want to simulate this authentication. So I created a FakeGuardAuthenticator which instead of interrogating the CAS server, simulates it and returns the credentials.

In my config_test.yml configuration file, I therefore have to override the guardAuthenticator by passing through an alias.

Which give :

# 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

Thansk to @vstelmakh and @Vyctorya for taking time to respond to me.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement