首页 > 解决方案 > 测试期间在 Django 视图中无法访问请求之前设置的会话变量

问题描述

我需要在 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. 在请求完成并且代码执行回到测试用例之后,在测试用例开头设置的会话变量仍然存在,因此测试失败。

我试图遵循文档中的规则,即我们需要在修改之前将会话存储到变量并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)

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

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

标签: pythondjangosession

解决方案


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

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

推荐阅读