diff --git a/django/test/client.py b/django/test/client.py index 6e0b443f83..f66b180867 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -1,4 +1,5 @@ from cStringIO import StringIO +from django.conf import settings from django.core.handlers.base import BaseHandler from django.core.handlers.wsgi import WSGIRequest from django.dispatch import dispatcher @@ -97,7 +98,8 @@ class Client: def __init__(self, **defaults): self.handler = ClientHandler() self.defaults = defaults - self.cookie = SimpleCookie() + self.cookies = SimpleCookie() + self.session = {} def request(self, **request): """ @@ -108,7 +110,7 @@ class Client: """ environ = { - 'HTTP_COOKIE': self.cookie, + 'HTTP_COOKIE': self.cookies, 'PATH_INFO': '/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', @@ -141,8 +143,14 @@ class Client: setattr(response, detail, None) if response.cookies: - self.cookie.update(response.cookies) + self.cookies.update(response.cookies) + if 'django.contrib.sessions' in settings.INSTALLED_APPS: + from django.contrib.sessions.middleware import SessionWrapper + cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) + if cookie: + self.session = SessionWrapper(cookie.value) + return response def get(self, path, data={}, **extra): diff --git a/docs/testing.txt b/docs/testing.txt index e7c1a3b161..9906749723 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -198,11 +198,6 @@ used as test conditions. .. _Twill: http://twill.idyll.org/ .. _Selenium: http://www.openqa.org/selenium/ -The Test Client is stateful; if a cookie is returned as part of a response, -that cookie is provided as part of the next request issued to that Client -instance. Expiry policies for these cookies are not followed; if you want -a cookie to expire, either delete it manually from ``client.cookies``, or -create a new Client instance (which will effectively delete all cookies). Making requests ~~~~~~~~~~~~~~~ @@ -296,6 +291,33 @@ for testing purposes: .. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +Persistent state +~~~~~~~~~~~~~~~~ + +The Test Client is stateful; if a cookie is returned as part of a response, +that cookie is provided as part of the next request issued by that Client +instance. Expiry policies for these cookies are not followed; if you want +a cookie to expire, either delete it manually or create a new Client +instance (which will effectively delete all cookies). + +There are two properties of the Test Client which are used to store persistent +state information. If necessary, these properties can be interrogated as +part of a test condition. + + =============== ========================================================== + Property Description + =============== ========================================================== + ``cookies`` A Python ``SimpleCookie`` object, containing the current + values of all the client cookies. + + ``session`` A dictionary-like object containing session information. + See the `session documentation`_ for full details. + +.. _`session documentation`: ../sessions/ + +Example +~~~~~~~ + The following is a simple unit test using the Test Client:: import unittest diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index c5b1a241ca..b6c3fbd5b1 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -99,3 +99,19 @@ class ClientTest(unittest.TestCase): response = self.client.login('/test_client/login_protected_view/', 'otheruser', 'nopassword') self.assertFalse(response) + + def test_session_modifying_view(self): + "Request a page that modifies the session" + # Session value isn't set initially + try: + self.client.session['tobacconist'] + self.fail("Shouldn't have a session value") + except KeyError: + pass + + from django.contrib.sessions.models import Session + response = self.client.post('/test_client/session_view/') + + # Check that the session was modified + self.assertEquals(self.client.session['tobacconist'], 'hovercraft') + \ No newline at end of file diff --git a/tests/modeltests/test_client/urls.py b/tests/modeltests/test_client/urls.py index 09bba5c007..5c77260c92 100644 --- a/tests/modeltests/test_client/urls.py +++ b/tests/modeltests/test_client/urls.py @@ -6,4 +6,5 @@ urlpatterns = patterns('', (r'^post_view/$', views.post_view), (r'^redirect_view/$', views.redirect_view), (r'^login_protected_view/$', views.login_protected_view), + (r'^session_view/$', views.session_view) ) diff --git a/tests/modeltests/test_client/views.py b/tests/modeltests/test_client/views.py index 7acfc2db60..406a814dec 100644 --- a/tests/modeltests/test_client/views.py +++ b/tests/modeltests/test_client/views.py @@ -32,4 +32,15 @@ def login_protected_view(request): c = Context({'user': request.user}) return HttpResponse(t.render(c)) -login_protected_view = login_required(login_protected_view) \ No newline at end of file +login_protected_view = login_required(login_protected_view) + +def session_view(request): + "A view that modifies the session" + + request.session['tobacconist'] = 'hovercraft' + + t = Template('This is a view that modifies the session.', + name='Session Modifying View Template') + c = Context() + return HttpResponse(t.render(c)) + \ No newline at end of file