Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#30101 closed Cleanup/optimization (invalid)

Recommended middleware syntax fails for some testing cases (when not using Client)

Reported by: Theodore Diamantidis Owned by: nobody
Component: Documentation Version: 2.1
Severity: Normal Keywords: middleware, unit test, requestfactory, request, layering
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description (last modified by Theodore Diamantidis)

It is common for developers to prefer RequestFactory over Client in unit testing, or any similar module that mocks requests without processing the URLs or applying the enabled middleware. So, when a unit test needs to test a view while using a middleware, the easiest way to do so is:

from django.test import RequestFactory

from .views import MyView
from .middleware import MyMiddleware

request = RequestFactory().get('/')
view = MyMiddleware(MyView)
response = view(request)

Also, it may be needed to test views that accept arguments (e.g. captured URL parameters), which would probably look like this:

request = RequestFactory().get('/articles/4/')
response = MyView(request, pk=4)

The problem arises when we both use test views that accept parameters and a custom middleware. The current documentation offers examples for writing such a middleware:

def MyMiddleware(get_response):
    # One-time configuration and initialization.

    def middleware(request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = get_response(request)

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

        return response

    return middleware

Given the above tests, one would expect this one to work as expected too:

request = RequestFactory().get('/articles/4/')
view = MyMiddleware(MyView)
response = view(request, pk=4)

However this is not the case, since the middleware function expects only a single positional argument and not the keyword argument pk, thus raising a TypeError.

The attached files recommend a more flexible (albeit less simple) syntax and data flow for custom middleware that would prevent inexperienced developers from getting stuck here.

Attachments (1)

middleware_syntax_in_docs.diff (1.4 KB ) - added by Theodore Diamantidis 6 years ago.
Patch for documentation

Download all attachments as: .zip

Change History (5)

by Theodore Diamantidis, 6 years ago

Patch for documentation

comment:1 by Theodore Diamantidis, 6 years ago

Description: modified (diff)

comment:2 by Prysiazhnyi Maksym, 6 years ago

Triage Stage: UnreviewedAccepted
Type: UncategorizedCleanup/optimization

in reply to:  1 comment:3 by Nasir Hussain, 6 years ago

Replying to Theodore Diamantidis:
Should I create a PR with the patch in the documentation you provided?
I don't know what's the next steps if someone uploads a patch file too.

comment:4 by Carlton Gibson, 6 years ago

Resolution: invalid
Status: newclosed

This usage isn't correct:

view = MyMiddleware(MyView)

It treats views and middlewares as if they were equivalent, which they are not.

Django's BaseHandler provides a _get_response() method that uses the URLConf and URLResolver to determine the callback (i.e. view function) plus the parameters to pass to it. (django/core/handlers/base.py.)

It's this _get_response() that sits at the center of the Middleware Onion™. You can't just pass in a view.

To simulate the full dispatch you'd need to do something like this, as a minimum:

request = RequestFactory().get('/articles/4/')
def _get_response(request):
    return MyView(request, pk=4)
middleware_chain = MyMiddleware(_get_response)

(Lots of ways we might make that better...)

I've thought about adding something on this to the middleware usage guide but, by the time you're jumping through these hoops, I think the best advice is to use the provided test client and let Django handle the mapping between your middleware and views.

Last edited 6 years ago by Carlton Gibson (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top