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:
Russell Keith-Magee 2007-02-17 00:23:09 +00:00
parent 4a85a75fb0
commit d6d51c9546
5 changed files with 60 additions and 21 deletions

View File

@ -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)

View File

@ -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')

View File

@ -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/')

View File

@ -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),

View File

@ -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/')