symfony/symfony — Issue #3287: Losing session cookie while using WebTestCase for PHPUnit tests
Author: intelliapps
Created: 2012-02-06 22:45:41+00:00
State: open

I am building Symfony2 apps on OSX Lion using PHP 5.3.6, MySQL 5.5.19 and Apache 2.2.0.
I have PHPUnit 3.6.10 installed and develop using Eclipse Helios 2 with XDebug 2.1.2.

I set up some functional tests for the app I'm working on as a PHPUnit test extending the Symfony WebTestCase class.

I used the Client and Crawler classes to try and run through the app as I would from within a browser. However, I encountered a situation where the app behaves differently under test than it does in the browser. I found that this is because the session cookie is lost (reset?) at a particular point in the tests whereas this does not occur when running the app in the browser.

After investigating this (and comparing it to the other Symfony apps I've worked on) I have found that the loss of the session cookie occurs when requesting a sequence of urls as follows:

/AAAA/name
…followed by...
/cart OR /BBBB OR /BBBB/name

These happen to be transitions between different controllers. I have wondered whether WebTestCase is not intended to support tests across multiple controllers although it does seem necessary for functional tests? However, I believe the problem could still occur if routes on a single controller had urls which vary like this, although I haven't come across this scenario myself.

I found this by analysing the following calls within the Symfony BrowserKit:
from CookieJar::updateFromResponse() (called by Client::request())
to Cookie::fromString()

1. IN: cookie = PHPSESSID=feb27189e1fe36592658c828f46a664d; expires=Sat, 04 Feb 2012 16:19:21 GMT, uri = http://calculator.dev/
1. OUT: Cookie::path = /
2. IN: cookie = PHPSESSID=feb27189e1fe36592658c828f46a664d; expires=Sat, 04 Feb 2012 16:20:32 GMT, uri = http://calculator.dev/plane
2. OUT: Cookie::path = /
3. IN: cookie = PHPSESSID=feb27189e1fe36592658c828f46a664d; expires=Sat, 04 Feb 2012 16:21:52 GMT, uri = http://calculator.dev/plane#
3. OUT: Cookie::path = /
4. IN: cookie = PHPSESSID=feb27189e1fe36592658c828f46a664d; expires=Sat, 04 Feb 2012 16:22:37 GMT, uri = http://calculator.dev/plane/display
4. OUT: Cookie::path = /plane
5. IN: cookie = PHPSESSID=feb27189e1fe36592658c828f46a664d; expires=Sat, 04 Feb 2012 16:24:10 GMT, uri = http://calculator.dev/plane/display#
5. OUT: Cookie::path = /plane
6. IN: cookie = PHPSESSID=15acaddee90b34f5027e1290d1476b70; expires=Sat, 04 Feb 2012 16:25:31 GMT, uri - http://calculator.dev/cart
6: OUT: Cookie::path = /

From this you can see that the session cookie is reset between requests for:
/plane/display#
and
/cart

The cookie is discarded by CookieJar::updateFromResponse() because the 'path' of the session cookie in the jar (/plane) does not match the path of the response uri (/cart).

I cannot suggest a fix for this as I don't really understand how cookies are supposed to work and whether the session cookie should be treated as a special case. However, I have been able to workaround this as follows:

Modify Symfony\Component\BrowserKit\CookieJar
Comment out the following lines from CookieJar::allValues():
123 //if ($cookie->getPath() != substr($parts['path'], 0, strlen($cookie->getPath()))) {
124 // continue;
125 //}

As far as I can tell CookieJar is only used for browser simulation and as my app only uses the session cookie this should not result in any false positive tests.