Setting a session variable in django tests

It 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 that calling self.client.login() self.client.session is just an empty dictionary. Is there a way to initialize it as a session object?

+7
django django-testing
source share
3 answers

If no cookies are set in the client, the session property is an empty dict, therefore, your mistake. Here is the corresponding django.test.client.Client source:

 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 did not enter the cookie with the key settings.SESSION_COOKIE_NAME , it was not found.

However, you can manually create a session object as follows:

 if not self.client.session: engine = import_module(settings.SESSION_ENGINE) self.client.session = engine.SessionStore() self.client.session.save() 

So the login handler in Client creates a new session.

EDIT: I realized that 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 that you can subclass in the Client class, which creates a new session and cookie with recommendations:

 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 am not saying that this is the only way to do this, it is one of the ways that I found out by reading the django source code. The code in this answer is not tested / running, so fine tuning may be required.

Further reading

To find out how SessionStore works, you can look at the django.contrib.sessions module.

To find out how sessions and cookies are handled in Client , you can look at django.test.client.Client .

+6
source share

Rzetterberg's answer is more stringent, so I think it should remain accepted, but it seems to 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 has been active for the past few months):
https://code.djangoproject.com/ticket/10899
and
https://code.djangoproject.com/ticket/11475

+3
source share

I used RequestFactory to create a request object, manually set the session values ​​and passed it to the view function, as indicated 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 create a request object allows you to test the view function, since you would test any other function - treating it as a black box, passing certain inputs and checking the correct output. Please note that you are executing the request separately, without any middleware installed.

0
source share

All Articles