如何正确地对Django中间件进行单元测试

3

我需要为几个Django中间件(Django > 1.10风格的中间件)编写单元测试。

项目是使用Django==2.2.3和djangorestframework==3.9.4实现的API,我正在使用标准的Django单元测试模块以及来自Django REST框架的APIRequestFactory()和APIClient()测试函数来创建用于我的测试的模拟请求。

这里是我想要测试的一个中间件的示例:

from . import models

class BrowserId():

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if hasattr(request, 'COOKIES') and 'mydomain.bid' in request.COOKIES:
            bid = request.COOKIES.get('mydomain.bid', None)
        else:
            bid = None

        request.bid = models.BrowserId(bid) if bid else models.BrowserId.random()

        response = self.get_response(request)

        response.set_cookie(
            conf.BID_COOKIE_KEY,
            value=request.bid,
            max_age=conf.COOKIE_MAX_AGE,
            domain=conf.BID_COOKIE_DOMAIN
        )

        return response

我希望进行测试:

  • 浏览器ID已成功添加到请求中

  • Cookie已成功设置在响应中

因此,更准确地说,测试的一部分应该大致如下:

from rest_framework.test import APIRequestFactory, APIClient, APITestCase
from my_api.auth import middlewares, models

class MiddlewaresTestCase(APITestCase):

    def test_fresh_browser_id_request(self):
        ??? SOME CODE TO PROCESS THE REQUEST BY THE MIDDLEWARE 
        assert hasattr(request, 'bid')
        assert len(request.bid) == 30

    def test_existing_browser_id_request(self):
        req = APIRequestFactory().get('/')
        mock_bid: str = 'abcd1234'
        req.COOKIES = {cookie_key: mock_bid}
        ??? SOME CODE TO PROCESS THE REQUEST BY THE MIDDLEWARE
        assert hasattr(req, 'bid')
        assert req.bid == mock_bid

    def test_browser_id_response(self):
        mock_bid: str = 'abc123'
        ??? SOME CODE TO PROCESS THE REQUEST BY THE MIDDLEWARE
        ??? SOME CODE TO PROCESS THE REPONSE BY THE MIDDLEWARE
        response = APIClient().get('/')
        assert res.cookies.get(cookie_key) is not None
        assert res.cookies.get(cookie_key).value == mock_bid

但是,正如你所看到的,我不知道如何单独完成这个任务。更确切地说:

  • 我不知道在初始化中间件时应该传递什么样的 get_response 函数。我知道它应该是处理视图函数或下一个中间件,但我不知道如何传递它,无论如何我都想测试它,而不用管其他内容;

  • 我不知道如何仅测试已处理的请求而不测试返回的响应。


在孤立地测试中间件并没有太多意义。只需通过调用一个你知道不会干扰请求的视图来进行正常的请求/响应循环,并检查结果中的 requestresponse 对象即可。 - dirkgroten
1个回答

4
test_browser_id_response中使用的APIClient可以模拟整个堆栈的请求,因此在测试中测试中间件非常理想。我建议您始终使用它,而不是APIFactory。然后,您可以断言响应的request属性。(注意,APITestCase已经为您实例化了客户端,您不需要创建新的客户端。)
def test_existing_browser_id_request(self):
    mock_bid: str = 'abcd1234'
    self.client.cookies[cookie_key] = mock_bid
    response = self.client.get('/')
    req = response.request
    self.assertTrue(hasattr(req, 'bid'))
    self.assertEqual(req.bid, mock_bid)

1
谢谢。看起来很简单,但由于它运行整个堆栈(因此通过所有中间件),它是否真的非常适合单元测试单个中间件而不受其他中间件交互的影响? - bolino
4
可能已经晚了,但你可以使用 @override_settings 装饰器将 MIDDLEWARE 类更改为仅包含你想要测试的中间件的列表。 - Oliver Monaghan-Coombs
1
我想补充一点,对于某些请求属性,可能需要使用response.wsgi_request属性。https://docs.djangoproject.com/en/3.0/topics/testing/tools/#django.test.Response.wsgi_request - Duilio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接