diff --git a/AUTHORS b/AUTHORS
index c690980c90..a0032f5198 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -284,6 +284,7 @@ answer newbie questions, and generally made Django that much better:
charly.wilhelm@gmail.com
Rachel Willmer
Gary Wilson
+ Jakub Wiśniowski
wojtek
ye7cakf02@sneakemail.com
ymasuda@ethercube.com
diff --git a/django/test/client.py b/django/test/client.py
index 7e62715205..9ec3615973 100644
--- a/django/test/client.py
+++ b/django/test/client.py
@@ -253,3 +253,14 @@ class Client:
else:
return False
+ def logout(self):
+ """Removes the authenticated user's cookies.
+
+ Causes the authenticated user to be logged out.
+ """
+ try:
+ Session.objects.get(session_key=self.cookies['sessionid'].value).delete()
+ except KeyError:
+ pass
+
+ self.cookies = SimpleCookie()
diff --git a/docs/testing.txt b/docs/testing.txt
index 4e723a1020..f189aa4578 100644
--- a/docs/testing.txt
+++ b/docs/testing.txt
@@ -550,6 +550,17 @@ Once you have a ``Client`` instance, you can call any of the following methods:
conditions. You'll need to create users as part of the test suite -- either
manually (using the Django model API) or with a test fixture.
+``logout()``
+ **New in Django development version**
+
+ If your site uses Django's `authentication system`_, the ``logout()``
+ method can be used to simulate the effect of a user logging out of
+ your site.
+
+ After you call this method, the test client will have all the cookies and
+ session data cleared to defaults. Subsequent requests will appear to
+ come from an AnonymousUser.
+
.. _authentication system: ../authentication/
.. _authentication backend: ../authentication/#other-authentication-sources
@@ -758,7 +769,7 @@ Here's specifically what will happen:
* At the start of each test case, before ``setUp()`` is run, Django will
flush the database, returning the database to the state it was in
directly after ``syncdb`` was called.
-
+
* Then, all the named fixtures are installed. In this example, Django will
install any JSON fixture named ``mammals``, followed by any fixture named
``birds``. See the `loaddata documentation`_ for more details on defining
@@ -843,7 +854,7 @@ The test runner accomplishes this by transparently replacing the normal
effect on any other e-mail senders outside of Django, such as your machine's
mail server, if you're running one.)
-During test running, each outgoing e-mail is saved in
+During test running, each outgoing e-mail is saved in
``django.core.mail.outbox``. This is a simple list of all `EmailMessage`_
instances that have been sent. It does not exist under normal execution
conditions, i.e., when you're not running unit tests. The outbox is created
@@ -977,7 +988,7 @@ a number of utility methods in the ``django.test.utils`` module.
``autoclobber`` describes the behavior that will occur if a database with
the same name as the test database is discovered:
-
+
* If ``autoclobber`` is ``False``, the user will be asked to approve
destroying the existing database. ``sys.exit`` is called if the user
does not approve.
diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py
index 951a41d61c..98b6a808a1 100644
--- a/tests/modeltests/test_client/models.py
+++ b/tests/modeltests/test_client/models.py
@@ -246,6 +246,22 @@ class ClientTest(TestCase):
login = self.client.login(username='inactive', password='password')
self.failIf(login)
+ def test_logout(self):
+ # Log in
+ self.client.login(username='testclient', password='password')
+
+ # Request a page that requires a login
+ response = self.client.get('/test_client/login_protected_view/')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context['user'].username, 'testclient')
+
+ # Log out
+ self.client.logout()
+
+ # Request a page that requires a login
+ response = self.client.get('/test_client/login_protected_view/')
+ self.assertRedirects(response, '/accounts/login/')
+
def test_session_modifying_view(self):
"Request a page that modifies the session"
# Session value isn't set initially