Sonntag, 7. Oktober 2012

symfony2: Testing secure pages

If you develop a web application, more often than not you have some kind of user section or admin panel where some kind of login identifies the user and protects your actions against usage from unauthorized people. It can be difficult to do functional tests with this kind of pages as you need to simulate some session or cookie context.

In this tutorial, I want to show you how to test your functional pages with symfony2 and phpunit. If you are experienced, this cookbook explanation might be sufficient: How to simulate HTTP Authentication in a Functional Test. For me it wasn't, first because I didn't found it and second because it is very short.

The bad idea

Let me start by telling you what I did as I didn't knew about this http authentication shortcut: I created a method login() which used a client, requested the login page, entered the credentials into the form, submited them and then returned. With this function I had a logged in client and could proceed with my test.

There are several reasons why this is bad:
  • A problem on the login page causes many tests to fail
  • It's complicated code for such a thing
  • It's very slow as for one test there have to be several requests (login page, login submit, redirect to a page from login, then the real page which should be tested)
For me, the third one was the worst, because it started to take really long to execute all functional tests and I knew it was mostly because there where so much going on which was not related to the particular test.

Using HTTP Authentication

The idea behind this concept is to set up your firewall in your test environment in a way that it allows http authentication and to send them with the first request, causing only a minor overhead of checking the credentials.

The symfony2 security layer comes with build in support for http authentification, so activating it in your test environment is a piece of cake. I created a security_test.yml file:

            http_basic: true
            stateless: false 

Then you include it in your config_test.yml:

    - { resource: config_dev.yml }
    - { resource: security_test.yml } 

Changing your test base class

I hope you have your own WebTestCase which you extend in your current TestCases, because it makes life much easier. You can then add a method which does the calls the request() method from you testclient with the correct credentials:

protected function requestWithAuth($client, $method, $uri, $parameters = array())
    $server = array('PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password');

    $client->request($method, $uri, $parameters, array(), $server);

In your test, instead of calling $client->request() directly, you call $this->requestWithAuth() and your request will arrive at the controller as if the user was logged. All following requests (like form submits and redirects) will also comming from a logged in client.

Going further

Of course you can go further with this, making the user as you log in configurable (depending on your need to test different roles or other user specific behaviour). You could also create your own Clients with login specific behaviour, if you need that. This might come handy if you have many different roles and the Client you are using makes this clear (UserClient, AdminClient) and so on, handling the authentication stuff within the specific client.

Even if only used with this one method, the way described here is valuable because the tests are cleaner, less error prone and execute way faster then by going through the login process everytime a user session is needed.

1 Kommentar:

  1. Das ist großartig, wie Sie erfunden, um vollständig freizulegen das Thema, dass Sie sich für genau diesen Eintrag abgeholt. Übrigens hast du auf alle gleichermaßen Artikeln als Quelle von Ideen drehen, um die gesamte Bild, das Sie in Ihrem Blog-Eintrag vorgesehen abzuschließen?