diff --git a/_build/redirection_map b/_build/redirection_map index 62b14b0954d..b9a72a200c7 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -325,3 +325,4 @@ /deployment/tools /deployment /install/bundles /setup/bundles /form /forms +/testing/simulating_authentication /testing/http_authentication diff --git a/testing/http_authentication.rst b/testing/http_authentication.rst index c201562a017..986fdaf3506 100644 --- a/testing/http_authentication.rst +++ b/testing/http_authentication.rst @@ -4,26 +4,24 @@ How to Simulate HTTP Authentication in a Functional Test ======================================================== -If your application needs HTTP authentication, pass the username and password -as server variables to ``createClient()``:: +Authenticating requests in functional tests can slow down the entire test suite. +This could become an issue especially when the tests reproduce the same steps +that users follow to authenticate, such as submitting a login form or using +OAuth authentication services. - $client = static::createClient(array(), array( - 'PHP_AUTH_USER' => 'username', - 'PHP_AUTH_PW' => 'pa$$word', - )); +This article explains the two most popular techniques to avoid these issues and +create fast tests when using authentication. -You can also override it on a per request basis:: +Using a Faster Authentication Mechanism Only for Tests +------------------------------------------------------ - $client->request('DELETE', '/post/12', array(), array(), array( - 'PHP_AUTH_USER' => 'username', - 'PHP_AUTH_PW' => 'pa$$word', - )); +When your application is using a ``form_login`` authentication, you can make +your tests faster by allowing them to use HTTP authentication. This way your +tests authenticate with the simple and fast HTTP Basic method whilst your real +users still log in via the normal login form. -When your application is using a ``form_login``, you can simplify your tests -by allowing your test configuration to make use of HTTP authentication. This -way you can use the above to authenticate in tests, but still have your users -log in via the normal ``form_login``. The trick is to include the ``http_basic`` -key in your firewall, along with the ``form_login`` key: +The trick is to use the ``http_basic`` authentication in your application +firewall, but only in the configuration file used by tests: .. configuration-block:: @@ -54,3 +52,72 @@ key in your firewall, along with the ``form_login`` key: ), ), )); + +Tests can now authenticate via HTTP passing the username and password as server +variables using the second argument of ``createClient()``:: + + $client = static::createClient(array(), array( + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'pa$$word', + )); + +The username and password can also be passed on a per request basis:: + + $client->request('DELETE', '/post/12', array(), array(), array( + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'pa$$word', + )); + +Creating the Authentication Token +--------------------------------- + +If your application uses a more advanced authentication mechanism, you can't +use the previous trick, but it's still possible to make tests faster. The trick +now is to bypass the authentication process, create the *authentication token* +yourself and store it in the session. + +This technique requires some knowledge of the security component internals, +but the following example shows a complete example that you can adapt to your +needs:: + + // src/AppBundle/Tests/Controller/DefaultControllerTest.php + namespace Appbundle\Tests\Controller; + + use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + use Symfony\Component\BrowserKit\Cookie; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; + + class DefaultControllerTest extends WebTestCase + { + private $client = null; + + public function setUp() + { + $this->client = static::createClient(); + } + + public function testSecuredHello() + { + $this->logIn(); + $crawler = $this->client->request('GET', '/admin'); + + $this->assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode()); + $this->assertSame('Admin Dashboard', $crawler->filter('h1')->text()); + } + + private function logIn() + { + $session = $this->client->getContainer()->get('session'); + + // the firewall context defaults to the firewall name + $firewallContext = 'secured_area'; + + $token = new UsernamePasswordToken('admin', null, $firewallContext, array('ROLE_ADMIN')); + $session->set('_security_'.$firewallContext, serialize($token)); + $session->save(); + + $cookie = new Cookie($session->getName(), $session->getId()); + $this->client->getCookieJar()->set($cookie); + } + } diff --git a/testing/simulating_authentication.rst b/testing/simulating_authentication.rst deleted file mode 100644 index 0d54776823f..00000000000 --- a/testing/simulating_authentication.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. index:: - single: Tests; Simulating authentication - -How to Simulate Authentication with a Token in a Functional Test -================================================================ - -Authenticating requests in functional tests might slow down the suite. -It could become an issue especially when ``form_login`` is used, since -it requires additional requests to fill in and submit the form. - -One of the solutions is to configure your firewall to use ``http_basic`` in -the test environment as explained in :doc:`/testing/http_authentication`. -Another way would be to create a token yourself and store it in a session. -While doing this, you have to make sure that an appropriate cookie is sent -with a request. The following example demonstrates this technique:: - - // src/AppBundle/Tests/Controller/DefaultControllerTest.php - namespace Appbundle\Tests\Controller; - - use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; - use Symfony\Component\BrowserKit\Cookie; - use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; - - class DefaultControllerTest extends WebTestCase - { - private $client = null; - - public function setUp() - { - $this->client = static::createClient(); - } - - public function testSecuredHello() - { - $this->logIn(); - - $crawler = $this->client->request('GET', '/admin'); - - $this->assertTrue($this->client->getResponse()->isSuccessful()); - $this->assertGreaterThan(0, $crawler->filter('html:contains("Admin Dashboard")')->count()); - } - - private function logIn() - { - $session = $this->client->getContainer()->get('session'); - - // the firewall context (defaults to the firewall name) - $firewall = 'secured_area'; - - $token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN')); - $session->set('_security_'.$firewall, serialize($token)); - $session->save(); - - $cookie = new Cookie($session->getName(), $session->getId()); - $this->client->getCookieJar()->set($cookie); - } - } - -.. note:: - - The technique described in :doc:`/testing/http_authentication` - is cleaner and therefore the preferred way.