Fixed #3160 -- Added the ability to control the content type in a test client POST request. This is to allow easier testing of json-rpc/xml-rpc/soap etc interfaces. Thanks to Mikeal Rogers for the suggestion, and Ben <afternoon@uk2.net> for the patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@4529 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
4a85a75fb0
commit
d6d51c9546
|
@ -9,6 +9,9 @@ from django.http import urlencode, SimpleCookie
|
||||||
from django.test import signals
|
from django.test import signals
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
|
|
||||||
|
BOUNDARY = 'BoUnDaRyStRiNg'
|
||||||
|
MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
|
||||||
|
|
||||||
class ClientHandler(BaseHandler):
|
class ClientHandler(BaseHandler):
|
||||||
"""
|
"""
|
||||||
A HTTP Handler that can be used for testing purposes.
|
A HTTP Handler that can be used for testing purposes.
|
||||||
|
@ -184,19 +187,20 @@ class Client:
|
||||||
|
|
||||||
return self.request(**r)
|
return self.request(**r)
|
||||||
|
|
||||||
def post(self, path, data={}, **extra):
|
def post(self, path, data={}, content_type=MULTIPART_CONTENT, **extra):
|
||||||
"Request a response from the server using POST."
|
"Request a response from the server using POST."
|
||||||
|
|
||||||
BOUNDARY = 'BoUnDaRyStRiNg'
|
if content_type is MULTIPART_CONTENT:
|
||||||
|
post_data = encode_multipart(BOUNDARY, data)
|
||||||
|
else:
|
||||||
|
post_data = data
|
||||||
|
|
||||||
encoded = encode_multipart(BOUNDARY, data)
|
|
||||||
stream = StringIO(encoded)
|
|
||||||
r = {
|
r = {
|
||||||
'CONTENT_LENGTH': len(encoded),
|
'CONTENT_LENGTH': len(post_data),
|
||||||
'CONTENT_TYPE': 'multipart/form-data; boundary=%s' % BOUNDARY,
|
'CONTENT_TYPE': content_type,
|
||||||
'PATH_INFO': path,
|
'PATH_INFO': path,
|
||||||
'REQUEST_METHOD': 'POST',
|
'REQUEST_METHOD': 'POST',
|
||||||
'wsgi.input': stream,
|
'wsgi.input': StringIO(post_data),
|
||||||
}
|
}
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
|
|
||||||
|
|
|
@ -217,15 +217,21 @@ can be invoked on the ``Client`` instance.
|
||||||
|
|
||||||
http://yoursite.com/customers/details/?name=fred&age=7
|
http://yoursite.com/customers/details/?name=fred&age=7
|
||||||
|
|
||||||
``post(path, data={})``
|
``post(path, data={}, content_type=MULTIPART_CONTENT)``
|
||||||
Make a POST request on the provided ``path``. The key-value pairs in the
|
Make a POST request on the provided ``path``. If you provide a content type
|
||||||
data dictionary will be used to create the POST data payload. This payload
|
(e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be
|
||||||
will be transmitted with the mimetype ``multipart/form-data``.
|
sent as-is in the POST request, using the content type in the HTTP
|
||||||
|
``Content-Type`` header.
|
||||||
However submitting files is a special case. To POST a file, you need only
|
|
||||||
|
If you do not provide a value for ``content_type``, the values in
|
||||||
|
``data`` will be transmitted with a content type of ``multipart/form-data``.
|
||||||
|
The key-value pairs in the data dictionary will be encoded as a multipart
|
||||||
|
message and used to create the POST data payload.
|
||||||
|
|
||||||
|
Submitting files is a special case. To POST a file, you need only
|
||||||
provide the file field name as a key, and a file handle to the file you wish to
|
provide the file field name as a key, and a file handle to the file you wish to
|
||||||
upload as a value. The Test Client will populate the two POST fields (i.e.,
|
upload as a value. The Test Client will populate the two POST fields (i.e.,
|
||||||
``field`` and ``field_file``) required by FileField. For example::
|
``field`` and ``field_file``) required by Django's FileField. For example::
|
||||||
|
|
||||||
c = Client()
|
c = Client()
|
||||||
f = open('wishlist.doc')
|
f = open('wishlist.doc')
|
||||||
|
|
|
@ -43,7 +43,7 @@ class ClientTest(unittest.TestCase):
|
||||||
|
|
||||||
# Check some response details
|
# Check some response details
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.template.name, 'Empty POST Template')
|
self.assertEqual(response.template.name, 'Empty GET Template')
|
||||||
|
|
||||||
def test_empty_post(self):
|
def test_empty_post(self):
|
||||||
"POST an empty dictionary to a view"
|
"POST an empty dictionary to a view"
|
||||||
|
@ -53,7 +53,7 @@ class ClientTest(unittest.TestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.template.name, 'Empty POST Template')
|
self.assertEqual(response.template.name, 'Empty POST Template')
|
||||||
|
|
||||||
def test_post_view(self):
|
def test_post(self):
|
||||||
"POST some data to a view"
|
"POST some data to a view"
|
||||||
post_data = {
|
post_data = {
|
||||||
'value': 37
|
'value': 37
|
||||||
|
@ -66,6 +66,14 @@ class ClientTest(unittest.TestCase):
|
||||||
self.assertEqual(response.template.name, 'POST Template')
|
self.assertEqual(response.template.name, 'POST Template')
|
||||||
self.failUnless('Data received' in response.content)
|
self.failUnless('Data received' in response.content)
|
||||||
|
|
||||||
|
def test_raw_post(self):
|
||||||
|
test_doc = """<?xml version="1.0" encoding="utf-8"?><library><book><title>Blink</title><author>Malcolm Gladwell</author></book></library>"""
|
||||||
|
response = self.client.post("/test_client/raw_post_view/", test_doc,
|
||||||
|
content_type="text/xml")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.template.name, "Book template")
|
||||||
|
self.assertEqual(response.content, "Blink - Malcolm Gladwell")
|
||||||
|
|
||||||
def test_redirect(self):
|
def test_redirect(self):
|
||||||
"GET a URL that redirects elsewhere"
|
"GET a URL that redirects elsewhere"
|
||||||
response = self.client.get('/test_client/redirect_view/')
|
response = self.client.get('/test_client/redirect_view/')
|
||||||
|
|
|
@ -4,6 +4,7 @@ import views
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
(r'^get_view/$', views.get_view),
|
(r'^get_view/$', views.get_view),
|
||||||
(r'^post_view/$', views.post_view),
|
(r'^post_view/$', views.post_view),
|
||||||
|
(r'^raw_post_view/$', views.raw_post_view),
|
||||||
(r'^redirect_view/$', views.redirect_view),
|
(r'^redirect_view/$', views.redirect_view),
|
||||||
(r'^login_protected_view/$', views.login_protected_view),
|
(r'^login_protected_view/$', views.login_protected_view),
|
||||||
(r'^session_view/$', views.session_view),
|
(r'^session_view/$', views.session_view),
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from xml.dom.minidom import parseString
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
@ -13,15 +14,34 @@ def post_view(request):
|
||||||
"""A view that expects a POST, and returns a different template depending
|
"""A view that expects a POST, and returns a different template depending
|
||||||
on whether any POST data is available
|
on whether any POST data is available
|
||||||
"""
|
"""
|
||||||
if request.POST:
|
if request.method == 'POST':
|
||||||
t = Template('Data received: {{ data }} is the value.', name='POST Template')
|
if request.POST:
|
||||||
c = Context({'data': request.POST['value']})
|
t = Template('Data received: {{ data }} is the value.', name='POST Template')
|
||||||
|
c = Context({'data': request.POST['value']})
|
||||||
|
else:
|
||||||
|
t = Template('Viewing POST page.', name='Empty POST Template')
|
||||||
|
c = Context()
|
||||||
else:
|
else:
|
||||||
t = Template('Viewing POST page.', name='Empty POST Template')
|
t = Template('Viewing GET page.', name='Empty GET Template')
|
||||||
c = Context()
|
c = Context()
|
||||||
|
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
def raw_post_view(request):
|
||||||
|
"""A view which expects raw XML to be posted and returns content extracted
|
||||||
|
from the XML"""
|
||||||
|
if request.method == 'POST':
|
||||||
|
root = parseString(request.raw_post_data)
|
||||||
|
first_book = root.firstChild.firstChild
|
||||||
|
title, author = [n.firstChild.nodeValue for n in first_book.childNodes]
|
||||||
|
t = Template("{{ title }} - {{ author }}", name="Book template")
|
||||||
|
c = Context({"title": title, "author": author})
|
||||||
|
else:
|
||||||
|
t = Template("GET request.", name="Book GET template")
|
||||||
|
c = Context()
|
||||||
|
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
def redirect_view(request):
|
def redirect_view(request):
|
||||||
"A view that redirects all requests to the GET view"
|
"A view that redirects all requests to the GET view"
|
||||||
return HttpResponseRedirect('/test_client/get_view/')
|
return HttpResponseRedirect('/test_client/get_view/')
|
||||||
|
|
Loading…
Reference in New Issue