- Author:
- robmadole
- Posted:
- November 12, 2010
- Language:
- Python
- Version:
- 1.2
- Score:
- 0 (after 0 ratings)
Testing low-level functionality sometimes requires a WSGIRequest object. An example of this is testing template tags.
This will monkey-patch the test Client object to return WSGIRequest objects
Normal Django behavior:
>>> client.get('/')
<HttpResponse >
With this code, get the request object:
>>> client.request_from.get('/')
<WSGIRequest >
Installation:
For this to work, you simply need to import the contents of this file.
If you name this file clientrequestpatch.py
, do this inside your Django tests.
from django.test.testcases import TestCase
from myproject.test import clientrequestpatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | from functools import wraps
from django.core.handlers.wsgi import WSGIRequest
from django.core.signals import got_request_exception
from django.template import TemplateDoesNotExist
from django.utils.functional import curry
from django.test import signals
from django.test.client import Client, store_rendered_templates
from django.core.handlers.base import BaseHandler
def new_init(f):
"""
We need to add a handler for returning the request instead of the response
and initialize a variable that will tell us whether a particular
get/post/put/delete is supposed to return the HttpResponse or the
WSGIRequest
"""
@wraps(f)
def wrapper(self, **defaults):
# It's an unbound function at this point, so we hook it with what we
# get off the wrapper
f(self, **defaults)
self._return_request_instead = False
self.handler_request = ClientHandler()
return wrapper
def switch_client_request_instead(self):
"""
Allows us to throw an internal switch in the Client to return the next
request as WSGIRequest instead of the HttpResponse
"""
self._return_request_instead = True
return self
def new_request(f):
"""
Handles actually creating a WSGIRequest instead of an HttpResponse if we
are switched into that mode. Will call the original request instead if
needed.
"""
def wrapper(self, **request):
if not self._return_request_instead:
# We are not supposed to return the request object itself, but do
# the normal Django test client request
return self.original_request(**request)
# So we are switched into a mode where we need to return the request
# instead of the response, we need to set the test client back to
# response-returning normality so the next call to this client doesn't
# continue to return WSGIRequest objects
self._return_request_instead = False
# Rip-off of the Django test client, but changed to return a fake WSGIRequest object
environ = {
'HTTP_COOKIE': self.cookies.output(header='', sep='; '),
'PATH_INFO': '/',
'QUERY_STRING': '',
'REMOTE_ADDR': '127.0.0.1',
'REQUEST_METHOD': 'GET',
'SCRIPT_NAME': '',
'SERVER_NAME': 'testserver',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.version': (1,0),
'wsgi.url_scheme': 'http',
'wsgi.errors': self.errors,
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
}
environ.update(self.defaults)
environ.update(request)
# Curry a data dictionary into an instance of the template renderer
# callback function.
data = {}
on_template_render = curry(store_rendered_templates, data)
signals.template_rendered.connect(on_template_render)
# Capture exceptions created by the handler.
got_request_exception.connect(self.store_exc_info)
try:
request = self.handler_request(environ)
except TemplateDoesNotExist, e:
# If the view raises an exception, Django will attempt to show
# the 500.html template. If that template is not available,
# we should ignore the error in favor of re-raising the
# underlying exception that caused the 500 error. Any other
# template found to be missing during view error handling
# should be reported as-is.
if e.args != ('500.html',):
raise
return request
return wrapper
class ClientHandler(BaseHandler):
"""
A HTTP Handler that can be used for testing purposes.
Uses the WSGI interface to compose requests, but returns
the raw HttpResponse object
"""
def __call__(self, environ):
from django.conf import settings
from django.core import signals
# Set up middleware if needed. We couldn't do this earlier, because
# settings weren't available.
if self._request_middleware is None:
self.load_middleware()
signals.request_started.send(sender=self.__class__)
try:
request = WSGIRequest(environ)
response = self.get_response(request)
# Apply response middleware.
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
finally:
signals.request_finished.disconnect(close_connection)
signals.request_finished.send(sender=self.__class__)
signals.request_finished.connect(close_connection)
return request
# Time to do some monkey-patching.
#
# This allows us to do this with the client object
#
# >>> client.get('/')
# <HttpResponse >
# >>> client.request_from.get('/')
# <WSGIRequest >
#
setattr(Client, 'request_from', property(switch_client_request_instead))
setattr(Client, '__init__', new_init(Client.__init__))
setattr(Client, 'original_request', Client.request)
setattr(Client, 'request', new_request(Client.request))
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 4 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.