#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 )
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)
Change History (5)
by , 6 years ago
Attachment: | middleware_syntax_in_docs.diff added |
---|
follow-up: 3 comment:1 by , 6 years ago
Description: | modified (diff) |
---|
comment:2 by , 6 years ago
Triage Stage: | Unreviewed → Accepted |
---|---|
Type: | Uncategorized → Cleanup/optimization |
comment:3 by , 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 , 6 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
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.
Patch for documentation