3

I am learning how to unittest Django middleware.

In the 'old style' Middleware, it was easy enough to load middleware using process_request() to test results. E.g.

def test_session(self):
    request = self.factory.get('/')
    session_middleware = SessionMiddleware()
    session_middleware.process_request(request)
    // Do stuff

However, in the 'new style' this does not exist. For example, how would I test the following example Django docs provide?

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

    def __call__(self, request):
        tzname = request.session.get('django_timezone')
        if tzname:
            timezone.activate(pytz.timezone(tzname))
        else:
            timezone.deactivate()
        return self.get_response(request)

Using TimezoneMiddleware(request) will call the __init__ but not the __call__?

alias51
  • 8,178
  • 22
  • 94
  • 166

1 Answers1

7

As you can see, the middleware simply implements a __call__ magic method. This means the instance of this class is callable.

The difference between the old-style and new-style middleware is that the new middleware is simply a callable that returns a callable - first you call it with a get_response callback, and then you call the returned callable with the actual request. get_response is a callable provided/injected by Django itself, and it's a function used to either return the view response, or the next middleware in the chain.

So, in order to test your SessionMiddleware, you could do the following:

import mock

def test_middleware(self):
    get_response = mock.MagicMock()
    request = self.factory.get('/')

    middleware = SessionMiddleware(get_response)
    response = middleware(request)

    # ensure get_response has been returned 
    # (or not, if your middleware does something else)
    self.assertEqual(get_response.return_value, response)
    
samu
  • 2,870
  • 16
  • 28
  • Thanks, this returns `response = middleware(request)` with `AttributeError: 'WSGIRequest' object has no attribute 'session'` ? – alias51 Jul 17 '20 at 09:10
  • 1
    What middleware are you testing? Because `SessionMiddleware` is the one that attaches the session object to the request: https://github.com/django/django/blob/master/django/contrib/sessions/middleware.py#L24 - if you're testing another middleware that depends on the session middleware, you have to either use session middleware first or mock the `.session` attribute on the request. – samu Jul 17 '20 at 11:01
  • I'm testing the `TimezoneMiddleware` shown in the question, which requires a session? – alias51 Jul 17 '20 at 11:13
  • 1
    Yes, you can see it in this line: `tzname = request.session.get('django_timezone')` - it requires a session to be attached to the request beforehand. – samu Jul 17 '20 at 21:50