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.