Search code examples
djangounit-testingdjango-rest-frameworkmiddleware

How to properly unit test a Django middleware


I have to write unit tests for several Django Middlewares (Django > 1.10 styled middlewares).

Project is an API, done with Django==2.2.3 and djangorestframework==3.9.4, I'm using standard Django unit testing module, and APIRequestFactory() and APIClient() test functions from Django REST framework to create mock requests for my tests.

Here is an example of one middleware I want to test:

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

I want to test:

  • That browser ID has successfully been added to the request

  • That cookie has successfully been set on the response

So, to be more precise, part of the tests would vaguely have to look like this:

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

etc.

But, as one can see, I don't know exactly how to individually do that. More precisely:

  • I don't know what to pass as a get_response function when initializing the middleware. I know it is supposed to be the processing view function, or the next middleware, but I don't know how to pass that and anyway I want to test it regardless of the rest;

  • I don't know how to test only the processed request and not the returned response.


Solution

  • The APIClient, which you use in test_browser_id_response, makes a simulated request through the whole stack and is therefore ideal in testing middleware. I would use that consistently rather than the APIFactory. You can then assert on the request attribute of the response. (Note, APITestCase already instantiates a client for you, you don't need to create a new one.)

    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)