pythondjangotestingdjango-middlewaredjango-1.10

Testing custom Django middleware without using Django itself


I have coded my custom Django middleware in the 1.10 style, similar to this:

class MyMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # some initialization stuff here

    def __call__(self, request):
        # Code executed before view functions are called. 
        # Purpose of this middeware is to add new attribute to request

        # In brief:
        request.new_attribute = some_function_returning_some_object()
        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

Note, that this middleware is being threaten as a separate Python module, not belonging to any particular application in my project, but living outside and being installed like any other package, via pip. It does not work itself, but only if installed in Django app.

It works fine, however, I would like to test it. What I've made so far is something like this in my_tests.py:

from my_middleware_module import MyMiddleware
# some @patches
def test_mymiddleware():
    request = Mock()
    assert hasattr(request, 'new_attribute') is False # passes obviously
    # CALL MIDDLEWARE ON REQUEST HERE
    assert hasattr(request, 'new_attribute') is True # I want it to pass

I don't know how to call middleware on request variable to modify it. I think it would be much easier if I used function-like middleware style, but what if I'm stuck with what I have and I am supposed ony to write tests, without modifying middleware?


Solution

  • The problem is that you are not calling neither the constructor of MyMiddleware neither invoking the __call__ magic method by invoking the instance of a MyMiddleware object.

    There are many ways to test the behaviour that you described, I can think of this one:

    First, I slightly modified your example to be self contained:

    class MyMiddleware(object):
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            request.new_attribute = some_function_returning_some_object()
            response = self.get_response(request)
            return response
    
    def some_function_returning_some_object():
        return 'whatever'
    

    Next, I created the tests by actually creating the Middleware object and invoking the newly created object as it was a function (so __call__ is run)

    from mock import patch, Mock
    from middle import MyMiddleware
    import unittest
    
    
    class TestMiddleware(unittest.TestCase):
    
        @patch('middle.MyMiddleware')
        def test_init(self, my_middleware_mock):
            my_middleware = MyMiddleware('response')
            assert(my_middleware.get_response) == 'response'
    
        def test_mymiddleware(self):
            request = Mock()
            my_middleware = MyMiddleware(Mock())
            # CALL MIDDLEWARE ON REQUEST HERE
            my_middleware(request)
            assert request.new_attribute == 'whatever'
    

    Here there are some useful links: