Установка переменной сеанса в тестах 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 это просто пустой словарь. Есть ли способ инициализировать его как объект сеанса?


person Ben    schedule 05.08.2014    source источник
comment
Какую версию джанго вы используете?   -  person rzetterberg    schedule 05.08.2014
comment
заморозка пипов › Django==1.6.5   -  person Ben    schedule 05.08.2014
comment
См. мое редактирование для полного объяснения.   -  person rzetterberg    schedule 05.08.2014


Ответы (4)


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

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

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

person Brachamul    schedule 15.04.2018
comment
Я считаю, что это исправлено в следующем коммит. Убедитесь, что вы поместили сеанс в переменную, прежде чем вносить изменения, как в ответе, поскольку каждый раз, когда вы пишете self.client.session, вы получаете новый объект. - person x-yuri; 06.04.2019
comment
@x-yuri, не могли бы вы написать пример, чтобы я мог заменить свой ответ? - person Brachamul; 06.04.2019
comment
Ваш пример в порядке, я просто добавил некоторые детали. - person x-yuri; 06.04.2019

Когда в клиенте не установлены файлы cookie, свойство 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.

person rzetterberg    schedule 05.08.2014
comment
Я удивлен, что это такая сложная задача. Кажется, что-то, что было бы обычно необходимо. Есть ли лучший подход для проведения такого рода тестирования? - person Ben; 06.08.2014
comment
@Ben Честно говоря, я не понимаю, зачем вам когда-либо нужно устанавливать значение cookie внутри теста. Чего вы пытаетесь достичь? - person rzetterberg; 06.08.2014
comment
У меня есть несколько представлений, в которых отображаемая информация зависит от местоположения, которое выбирает пользователь. В качестве очень грубого примера: пользователя интересует район Лос-Анджелеса. Они могли бы открыть вид, показывающий места для обеда. Затем, не выбирая повторно свое местоположение, они могли открыть другой вид баров. Я решил, что лучший способ — передать их местоположение в переменной сеанса. Я хотел написать несколько тестов, чтобы убедиться, что определенные функции работают, когда местоположение выбрано, а когда нет. Я все еще новичок в веб-разработке, это правильный путь? - person Ben; 06.08.2014
comment
@ Бен, я вижу, это звучит разумно. На мой взгляд, было бы лучше использовать тест Selenium, который фактически выбирает местоположение, используя форму, предоставленную представлением. Таким образом, ваш тест будет ближе к тому, как реальный пользователь взаимодействует с вашим сайтом. Подробнее о Selenium можно прочитать здесь: pypi.python.org/pypi/selenium - person rzetterberg; 06.08.2014
comment
Если клиентский сеанс уже существует, вам нужно будет использовать session = self.client.session\n session[key] = value\n session.save() Прямая ссылка на self.client.session[key] = value для меня не сработает - также упоминается в комментариях здесь - person Conor Svensson; 25.11.2015
comment
Неважно, существует сессия или нет, вы должны поместить self.client.session в переменную, внести изменения, затем save(). Не изменяйте self.client.session напрямую, так как каждый раз, когда вы пишете этот фрагмент кода, вы получаете новый объект. Более актуальный ответ. @Бен хочешь принять? - person x-yuri; 06.04.2019

ответ 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

person Ben    schedule 06.08.2014

Я использовал RequestFactory для создания объекта request, установить значения сеанса вручную и передать их функции просмотра, как указано в документы.

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 для создания объекта запроса позволяет вам тестировать функцию представления так же, как вы бы тестировали любую другую функцию — рассматривая ее как черный ящик, передавая определенные входные данные и проверяя правильность вывода. Обратите внимание, что вы выполняете запрос изолированно, без какого-либо промежуточного программного обеспечения.

person Jonathan Cox    schedule 08.12.2016