When you need to somehow keep the state of your Django application, one of the possible solutions is to use sessions. They let you overcome the weakness of HTTP, which is, well, stateless.

Django uses sessions by default for e.g. user authentication. You need this function if you want to let your users log in to your application. The framework stores session data inside a database and provides the user a cookie for the session id. Then, on every request, Django makes a special call to the database in order to get session data with the session id from the cookie (including your login data).

You may want to have the session data stored entirely in cookies, for example, in order to reduce database load. In order to achieve that, add following settings entry in your configuration file:

# settings.py

# ...

SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"

That’s all! But, is it? Now, the session data will be stored in cookies. Does it change anything besides the desired functionality? Well, it can affect your testing. Let’s make a very simple scenario that you have only one function view. This view will do two small things: return session value and change it. So, basically, we want to manipulate the session variable:

# views.py

from django.http import HttpResponse


def view_session_variable(request):
    session_value = request.session['foo']
    request.session['foo'] += 1
    return HttpResponse(session_value)

To make things as simple as possible, we’ll omit type check this time. Remember about adding the view to the routing:

# urls.py

from django.contrib import admin
from django.urls import path

from .views import view_session_variable

urlpatterns = [
    path('', view_session_variable, name='example_view'),
]

Simple, isn’t it? Now, let’s write a test:

# tests.py

from django.conf import settings as django_settings
from django.test import TestCase
from django.urls import reverse


class FooTestCase(TestCase):

    def test_with_client(self):
        """Verify if view returns session variable and increase by one."""

        # GIVEN
        DUMMY_INT = 2

        session = self.client.session
        session["foo"] = DUMMY_INT
        session.save()

        # WHEN
        response = self.client.get(reverse("example_view"))

        # THEN
        assert bytes.decode(response.content, 'utf8') == str(DUMMY_INT)
        assert self.client.session["foo"] == DUMMY_INT+1


According to Django documentation, when you want to access session before requests, you need to store it into a variable. That’s because the test client’s session is really a property that generates a new session object if there isn’t an existing one. Behind the scenes, after creating the session object, it’s assigned to a client’s cookie object. Going back to the test, in the next step, we assign value for session variable called foo. Finally, we run the test request and check the results of our view. You may be surprised to see the error: KeyError: 'foo'.

How could it be? Well, the answer is in the source code of the mentioned session property of test client:

# django/test/client.py

# ...

class Client(RequestFactory):

    # ...

    @property
    def session(self):
        """Return the current session variables."""
        engine = import_module(settings.SESSION_ENGINE)
        cookie = self.cookies.get(settings.SESSION_COOKIE_NAME)
        if cookie:
            return engine.SessionStore(cookie.value)
        session = engine.SessionStore()
        session.save()
        self.cookies[settings.SESSION_COOKIE_NAME] = session.session_key
        return session

If you take closer look, you will notice that in case the session cookie is empty, we assign to it the new session object. It’s very loose coupling, meaning that session object knows nothing about the cookie which contains it. So, if we change this session object, what we need is to update the cookie:

# tests.py

from django.conf import settings as django_settings
from django.test import TestCase
from django.urls import reverse


class FooTestCase(TestCase):

    def test_with_client(self):
        """Verify if view returns session variable and increase by one."""

        # GIVEN
        DUMMY_INT = 2

        session = self.client.session
        session["foo"] = DUMMY_INT
        session.save()

        # Update session's cookie
        session_cookie_name = django_settings.SESSION_COOKIE_NAME
        self.client.cookies[session_cookie_name] = session.session_key

        # WHEN
        response = self.client.get(reverse("example_view"))

        # THEN
        assert bytes.decode(response.content, 'utf8') == str(DUMMY_INT)
        assert self.client.session["foo"] == DUMMY_INT+1

That’s it! Now, if you run your test, it will pass 🙂

If you want to see the whole working example, please see my Github repository. Good luck with code coverage!