Установка переменной сеанса в тестах django


Это работает

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'))

Но это

def test_access_to_home_with_location(self):
    session = self.client.session
    session['location'] = [42]
    session.save()
    response = self.client.get(reverse('home'))

Разрывы с

====================================================================== 
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'

Так кажется без вызова self.client.login() self.client.session это просто пустой словарь. Есть ли способ инициализировать его как объект сеанса?

4   7   2014-08-05 10:41:58

4 ответа:

Когда в клиенте не установлены куки, свойство session является пустым диктом, отсюда и ваша ошибка. Вот соответствующий источник 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)

Поскольку вы не вошли в файл cookie, соответствующий ключу settings.SESSION_COOKIE_NAME не найден.

Однако вы можете вручную создать объект сеанса следующим образом:

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

    self.client.session = engine.SessionStore()
    self.client.session.save()

Именно так обработчик login в Client создает новый сеанс.

EDIT: я понял, что вам также нужно сохранить ключ сеанса в файле cookie, чтобы следующий запрос использует ту же сессию

Вот вспомогательная функция, которую вы можете поместить в свой подкласс Client, который создает новую сессию и файл 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()

Примечание: Я не говорю, что это единственный способ сделать это, это один из способов, который я обнаружил, прочитав исходный код django. Код в этом ответе не тестируется/выполняется, поэтому он может нуждаться в некоторой тонкой настройке.

Дальнейшее чтение

Чтобы прочитать о том, как работает SessionStore, вы можете посмотреть на Модуль django.contrib.sessions.

Чтобы прочитать о том, как обрабатываются сессии и файлы cookie в Client, Вы можете посмотреть на django.test.client.Client.

Ответ Rzetterberg является более строгим, поэтому я думаю, что он должен оставаться принятым, но этот способ, похоже, также будет работать

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

Похоже, что на эту тему открыт текущий тикет (начатый 5 лет назад... но активно в течение последних нескольких месяцев):
https://code.djangoproject.com/ticket/10899
и еще
https://code.djangoproject.com/ticket/11475

Обратите внимание, что обходные пути больше не нужны. Фрагмент исходного вопроса, который не работал, теперь должен работать:

session = self.client.session
session['location'] = [42]
session.save()

Https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.Client.session

Я использовал RequestFactory чтобы создать объект request, задайте значения сеанса вручную и передайте его в функцию view, как описано в документе 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

Использование RequestFactory для генерации объекта запроса позволяет тестировать функцию представления так же, как и любую другую функцию - рассматривать ее как черный ящик, передавать определенные входные данные и проверять правильность вывода. Обратите внимание, что вы выполняете запрос изолированно, без какого-либо установленного промежуточного программного обеспечения.