【问题标题】:Session variable set before request is not accessible in Django view during tests测试期间在 Django 视图中无法访问请求之前设置的会话变量
【发布时间】:2020-03-25 07:10:40
【问题描述】:

我需要在 HTTP 请求之间存储一种状态。因为我不使用 JS 前端,所以我决定使用存储在 cookie 中的会话变量。我的会话设置如下所示:

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

我需要一个函数来轻松操作一个会话变量。因为这个功能非常简单,所以我决定特意使用基于函数的视图:

def set_category_filter(request, pk):
    if pk == 0:
        if 'filter_category_id' in request.session:  # this is False during unit testing, but it's ok in a normal app run
            del request.session['filter_category_id']
    else:
        get_object_or_404(Category, pk=pk, user=request.user)
        request.session['filter_category_id'] = pk
    return redirect('index')

问题是当我想取消设置这个会话变量时,我无法测试用例。假设会话变量filter_category_id 已设置并且我想重置它。当我在浏览器中执行此操作时,一切正常。当我尝试通过 Django TestCase 执行此操作时,它失败了,因为此会话变量未传递给请求!

class SetCategoryFilterTest(TestCase):
    def setUp(self) -> None:
        super().setUp()
        self.fake = Faker()
        self.user = User.objects.create_user(username="testuser", password="12345")
        self.client.force_login(self.user)

    def tearDown(self):
        self.client.logout()

    # ...

    def test_unset_successfully(self):
        """Werify unsetting "filter_category_id" session field.

        Expected: Delete "filter_category_id" when url called with parameter 0
        """

        # GIVEN
        session = self.client.session
        session['filter_category_id'] = DUMMY_INT
        session.save()

        clear_arg = 0

        # WHEN
        self.client.get(reverse("filter_tasks_by_category", args=[clear_arg]))

        # THEN
        self.assertFalse("filter_category_id" in session)

如您所见,我尝试在测试中假设提到的会话变量已经设置。然后我尝试删除,但是有两个问题:

  1. 在调试过程中,条件if 'filter_category_id' in request.session等于False,所以会话变量没有被取消。

  2. 请求完成,代码执行回到测试用例,测试用例开头设置的会话变量仍然存在,测试失败。

我尝试遵循documentation 的规则,即我们需要在修改之前将会话存储到变量并在之后调用save()。但这没有帮助。另外,我在某处读到最好做另一个请求或至少login() 以设置会话对象。但是,正如您在setUp() 方法中看到的,登录已经完成。

当我尝试执行另一个请求以确保该会话已创建时,它也无济于事:

def test_unset_successfully(self):
    """Werify unsetting "filter_category_id" session field.

    Expected: Delete "filter_category_id" when url called with parameter 0
    """

    # GIVEN
    category = Category.objects.create(user=self.user, name=self.fake.name())
    self.client.get(reverse("filter_tasks_by_category", args=[category.id]))

    session = self.client.session
    session['filter_category_id'] = DUMMY_INT
    session.save()

    clear_arg = 0

    # WHEN
    self.client.get(reverse("filter_tasks_by_category", args=[clear_arg]))

    # THEN
    self.assertFalse("filter_category_id" in session)

这段代码不仅奇怪,而且没有确认访问会话对象之前必须先请求的语句。

你知道如何处理这种情况吗?基本上我想做的就是设置一个会话变量并将其与请求一起传递给我的视图。

【问题讨论】:

    标签: python django session


    【解决方案1】:

    如果您只想测试删除部分,请使用模拟:

    def test_unset_successfully(self):
        request_mock = Mock()
        request_mock.session = {"filter_category_id": 123}
    
        set_category_filter(request_mock, 0)
    
        self.assertIsNone(request_mock.session.get("filter_category_id"))
    

    【讨论】:

    • 是的,这个看起来是一个不错的替代品,但我想它有潜在的缺陷。这一次,其实我只是想测试一下字典修改。但另一次我需要对会话对象做一些更复杂的操作。感谢您的努力,您得到了我的 (+1)!但我想看看如何操作这个真正的会话对象。
    【解决方案2】:

    最后我发现我需要更新会话cookie:

    # GIVEN
    session = self.client.session
    session["filter_category_id"] = DUMMY_INT
    session.save()
    
    # After saving new session, it must be re-assigned to a session cookie
    session_cookie_name = django_settings.SESSION_COOKIE_NAME
    self.client.cookies[session_cookie_name] = session.session_key
    

    https://kurzacz.com/writing-django-tests-for-session-variables-stored-in-cookies/找到解决方案

    【讨论】:

      猜你喜欢
      • 2014-09-27
      • 1970-01-01
      • 1970-01-01
      • 2021-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多