问题
This works
def test_access_to_home_with_location(self):
self.client.login(username=self.user.get_username(), password='pass')
session = self.client.session
session['location'] = [42]
session.save()
response = self.client.get(reverse('home'))
But this
def test_access_to_home_with_location(self):
session = self.client.session
session['location'] = [42]
session.save()
response = self.client.get(reverse('home'))
breaks with
======================================================================
ERROR: test_access_to_home_with_location (posts.tests.HomeViewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 32, in test_access_to_home_with_location
session.save()
AttributeError: 'dict' object has no attribute 'save'
So it seems with out calling self.client.login()
self.client.session
is just an empty dictionary. Is there a way to initialize it as a session object?
回答1:
Please note that workarounds are no longer necessary. The original question's snippet which did not work should now work :
session = self.client.session
session['location'] = [42]
session.save()
https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.Client.session
回答2:
When no cookies are set in the client the session
property is a empty dict, hence your error. Here is the relevant source of django.test.client.Client
:
def _session(self):
"""
Obtains the current session variables.
"""
if 'django.contrib.sessions' in settings.INSTALLED_APPS:
engine = import_module(settings.SESSION_ENGINE)
cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
if cookie:
return engine.SessionStore(cookie.value)
return {}
session = property(_session)
Since you are not logged in the cookie with the key matching settings.SESSION_COOKIE_NAME
is not found.
However you could manually create a session object like this:
if not self.client.session:
engine = import_module(settings.SESSION_ENGINE)
self.client.session = engine.SessionStore()
self.client.session.save()
This is the way the login
handler in Client
creates a new session.
EDIT: I realized you also need to save the session key in a cookie so that the next request uses the same session
Here's a helper function you could put in your Client
sub class that creates a new session and a referencing cookie:
def set_session_data(self, key, value):
"""Shortcut for setting session data regardless of being authenticated"""
if not self.client.session:
# Save new session in database and add cookie referencing it
engine = import_module(settings.SESSION_ENGINE)
self.client.session = engine.SessionStore()
self.client.session.save()
session_cookie = settings.SESSION_COOKIE_NAME
self.client.cookies[session_cookie] = self.client.session.session_key
cookie_data = {
'max-age': None,
'path': '/',
'domain': settings.SESSION_COOKIE_DOMAIN,
'secure': settings.SESSION_COOKIE_SECURE or None,
'expires': None,
}
self.client.cookies[session_cookie].update(cookie_data)
self.client.session[key] = value
self.client.session.save()
Note: I'm not saying this is the only way to do this, this is one way I found out by reading the django source code. The code in this answer is not tested/run, therefor it might need some fine tuning.
Further reading
To read about how SessionStore
works you can look at the django.contrib.sessions
module.
To read about how session and cookies are handled in Client
you can look at django.test.client.Client
.
回答3:
rzetterberg answer is the more rigorous one so I think it should remain accepted, but this way looks like it will also work
def setUp(self):
"""
set up sessions for anonymous users
"""
engine = import_module(settings.SESSION_ENGINE)
store = engine.SessionStore()
store.save()
self.client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key
It looks like there is a current ticket open on this topic (started 5 years ago... but active within the last few months):
https://code.djangoproject.com/ticket/10899
and
https://code.djangoproject.com/ticket/11475
回答4:
I used RequestFactory to generate a request
object, set the session values manually, and passed it to the view function, as laid out in the docs.
from django.test import TestCase, RequestFactory
from django.urls import reverse
from .views import test_view
class MyTestCase(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_view_x(self):
request = self.factory.get(reverse('myapp:test_view'))
request.session = {'foo': 'bar'}
response = test_view(request)
...
# Run some test on the response to ensure the view is returning
# the expected value
Using RequestFactory
to generate a request object lets you test a view function as you would test any other function - treating it as a black box, passing certain inputs and checking for the correct output. Note that you're running the request in isolation, without any of the installed middleware.
来源:https://stackoverflow.com/questions/25132621/setting-a-session-variable-in-django-tests