Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #9351 -- Modified the test client to pass on URL encoded parame…

…ters to the underlying views. Thanks to sime for the suggestion.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9398 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 95d8c0619a961ebf55208bebe5cc686b8247655f 1 parent a53ccc8
@freakboy3742 freakboy3742 authored
View
31 django/test/client.py
@@ -1,4 +1,5 @@
import urllib
+from urlparse import urlparse, urlunparse
import sys
import os
try:
@@ -260,10 +261,11 @@ def get(self, path, data={}, **extra):
"""
Requests a response from the server using GET.
"""
+ parsed = urlparse(path)
r = {
'CONTENT_TYPE': 'text/html; charset=utf-8',
- 'PATH_INFO': urllib.unquote(path),
- 'QUERY_STRING': urlencode(data, doseq=True),
+ 'PATH_INFO': urllib.unquote(parsed.path),
+ 'QUERY_STRING': urlencode(data, doseq=True) or parsed.query,
'REQUEST_METHOD': 'GET',
'wsgi.input': FakePayload('')
}
@@ -280,10 +282,12 @@ def post(self, path, data={}, content_type=MULTIPART_CONTENT, **extra):
else:
post_data = data
+ parsed = urlparse(path)
r = {
'CONTENT_LENGTH': len(post_data),
'CONTENT_TYPE': content_type,
- 'PATH_INFO': urllib.unquote(path),
+ 'PATH_INFO': urllib.unquote(parsed.path),
+ 'QUERY_STRING': parsed.query,
'REQUEST_METHOD': 'POST',
'wsgi.input': FakePayload(post_data),
}
@@ -295,10 +299,11 @@ def head(self, path, data={}, **extra):
"""
Request a response from the server using HEAD.
"""
+ parsed = urlparse(path)
r = {
'CONTENT_TYPE': 'text/html; charset=utf-8',
- 'PATH_INFO': urllib.unquote(path),
- 'QUERY_STRING': urlencode(data, doseq=True),
+ 'PATH_INFO': urllib.unquote(parsed.path),
+ 'QUERY_STRING': urlencode(data, doseq=True) or parsed.query,
'REQUEST_METHOD': 'HEAD',
'wsgi.input': FakePayload('')
}
@@ -310,9 +315,10 @@ def options(self, path, data={}, **extra):
"""
Request a response from the server using OPTIONS.
"""
+ parsed = urlparse(path)
r = {
- 'PATH_INFO': urllib.unquote(path),
- 'QUERY_STRING': urlencode(data, doseq=True),
+ 'PATH_INFO': urllib.unquote(parsed.path),
+ 'QUERY_STRING': urlencode(data, doseq=True) or parsed.query,
'REQUEST_METHOD': 'OPTIONS',
'wsgi.input': FakePayload('')
}
@@ -328,11 +334,13 @@ def put(self, path, data={}, content_type=MULTIPART_CONTENT, **extra):
post_data = encode_multipart(BOUNDARY, data)
else:
post_data = data
+
+ parsed = urlparse(path)
r = {
'CONTENT_LENGTH': len(post_data),
'CONTENT_TYPE': content_type,
- 'PATH_INFO': urllib.unquote(path),
- 'QUERY_STRING': urlencode(data, doseq=True),
+ 'PATH_INFO': urllib.unquote(parsed.path),
+ 'QUERY_STRING': urlencode(data, doseq=True) or parsed.query,
'REQUEST_METHOD': 'PUT',
'wsgi.input': FakePayload(post_data),
}
@@ -344,9 +352,10 @@ def delete(self, path, data={}, **extra):
"""
Send a DELETE request to the server.
"""
+ parsed = urlparse(path)
r = {
- 'PATH_INFO': urllib.unquote(path),
- 'QUERY_STRING': urlencode(data, doseq=True),
+ 'PATH_INFO': urllib.unquote(parsed.path),
+ 'QUERY_STRING': urlencode(data, doseq=True) or parsed.query,
'REQUEST_METHOD': 'DELETE',
'wsgi.input': FakePayload('')
}
View
24 docs/topics/testing.txt
@@ -493,6 +493,18 @@ arguments at time of construction:
/customers/details/?name=fred&age=7
+ .. versionadded:: development
+
+ If you already have the GET arguments in URL-encoded form, you can
+ use that encoding instead of using the data argument. For example,
+ the previous GET request could also be posed as::
+
+ >>> c = Client()
+ >>> c.get('/customers/details/?name=fred&age=7')
+
+ If you provide URL both an encoded GET data and a data argument,
+ the data argument will take precedence.
+
.. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT)
Makes a POST request on the provided ``path`` and returns a
@@ -544,6 +556,18 @@ arguments at time of construction:
Note that you should manually close the file after it has been provided
to ``post()``.
+ .. versionadded:: development
+
+ If the URL you request with a POST contains encoded parameters, these
+ parameters will be made available in the request.GET data. For example,
+ if you were to make the request::
+
+ >>> c.post('/login/?vistor=true', {'name': 'fred', 'passwd': 'secret'})
+
+ ... the view handling this request could interrogate request.POST
+ to retrieve the username and password, and could interrogate request.GET
+ to determine if the user was a visitor.
+
.. method:: Client.head(path, data={})
.. versionadded:: development
View
61 tests/regressiontests/test_client_regress/models.py
@@ -309,17 +309,17 @@ def test_exception_cleared(self):
self.client.get("/test_client_regress/staff_only/")
except SuspiciousOperation:
self.fail("Staff should be able to visit this page")
-
+
class TemplateExceptionTests(TestCase):
def setUp(self):
self.old_templates = settings.TEMPLATE_DIRS
settings.TEMPLATE_DIRS = ()
-
+
def tearDown(self):
settings.TEMPLATE_DIRS = self.old_templates
-
+
def test_no_404_template(self):
- "Missing templates are correctly reported by test client"
+ "Missing templates are correctly reported by test client"
try:
response = self.client.get("/no_such_view/")
self.fail("Should get error about missing template")
@@ -334,7 +334,7 @@ def test_bad_404_template(self):
self.fail("Should get error about syntax error in template")
except TemplateSyntaxError:
pass
-
+
# We need two different tests to check URLconf substitution - one to check
# it was changed, and another one (without self.urls) to check it was reverted on
# teardown. This pair of tests relies upon the alphabetical ordering of test execution.
@@ -421,3 +421,54 @@ def test_delete(self):
response = self.client.delete('/test_client_regress/request_methods/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'request method: DELETE')
+
+class QueryStringTests(TestCase):
+ def test_get_like_requests(self):
+ for method_name in ('get','head','options','put','delete'):
+ # A GET-like request can pass a query string as data
+ method = getattr(self.client, method_name)
+ response = method("/test_client_regress/request_data/", data={'foo':'whiz'})
+ self.assertEqual(response.context['get-foo'], 'whiz')
+ self.assertEqual(response.context['request-foo'], 'whiz')
+
+ # A GET-like request can pass a query string as part of the URL
+ response = method("/test_client_regress/request_data/?foo=whiz")
+ self.assertEqual(response.context['get-foo'], 'whiz')
+ self.assertEqual(response.context['request-foo'], 'whiz')
+
+ # Data provided in the URL to a GET-like request is overridden by actual form data
+ response = method("/test_client_regress/request_data/?foo=whiz", data={'foo':'bang'})
+ self.assertEqual(response.context['get-foo'], 'bang')
+ self.assertEqual(response.context['request-foo'], 'bang')
+
+ response = method("/test_client_regress/request_data/?foo=whiz", data={'bar':'bang'})
+ self.assertEqual(response.context['get-foo'], None)
+ self.assertEqual(response.context['get-bar'], 'bang')
+ self.assertEqual(response.context['request-foo'], None)
+ self.assertEqual(response.context['request-bar'], 'bang')
+
+ def test_post_like_requests(self):
+ # A POST-like request can pass a query string as data
+ response = self.client.post("/test_client_regress/request_data/", data={'foo':'whiz'})
+ self.assertEqual(response.context['get-foo'], None)
+ self.assertEqual(response.context['post-foo'], 'whiz')
+
+ # A POST-like request can pass a query string as part of the URL
+ response = self.client.post("/test_client_regress/request_data/?foo=whiz")
+ self.assertEqual(response.context['get-foo'], 'whiz')
+ self.assertEqual(response.context['post-foo'], None)
+ self.assertEqual(response.context['request-foo'], 'whiz')
+
+ # POST data provided in the URL augments actual form data
+ response = self.client.post("/test_client_regress/request_data/?foo=whiz", data={'foo':'bang'})
+ self.assertEqual(response.context['get-foo'], 'whiz')
+ self.assertEqual(response.context['post-foo'], 'bang')
+ self.assertEqual(response.context['request-foo'], 'bang')
+
+ response = self.client.post("/test_client_regress/request_data/?foo=whiz", data={'bar':'bang'})
+ self.assertEqual(response.context['get-foo'], 'whiz')
+ self.assertEqual(response.context['get-bar'], None)
+ self.assertEqual(response.context['post-foo'], None)
+ self.assertEqual(response.context['post-bar'], 'bang')
+ self.assertEqual(response.context['request-foo'], 'whiz')
+ self.assertEqual(response.context['request-bar'], 'bang')
View
1  tests/regressiontests/test_client_regress/urls.py
@@ -5,6 +5,7 @@
(r'^no_template_view/$', views.no_template_view),
(r'^staff_only/$', views.staff_only_view),
(r'^get_view/$', views.get_view),
+ (r'^request_data/$', views.request_data),
url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'),
(r'^login_protected_redirect_view/$', views.login_protected_redirect_view),
(r'^set_session/$', views.set_session_view),
View
12 tests/regressiontests/test_client_regress/views.py
@@ -1,6 +1,7 @@
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect
from django.core.exceptions import SuspiciousOperation
+from django.shortcuts import render_to_response
def no_template_view(request):
"A simple view that expects a GET request, and returns a rendered template"
@@ -18,6 +19,17 @@ def get_view(request):
return HttpResponse("Hello world")
get_view = login_required(get_view)
+def request_data(request):
+ "A simple view that returns the request data in the context"
+ return render_to_response('base.html', {
+ 'get-foo':request.GET.get('foo',None),
+ 'get-bar':request.GET.get('bar',None),
+ 'post-foo':request.POST.get('foo',None),
+ 'post-bar':request.POST.get('bar',None),
+ 'request-foo':request.REQUEST.get('foo',None),
+ 'request-bar':request.REQUEST.get('bar',None),
+ })
+
def view_with_argument(request, name):
"""A view that takes a string argument
Please sign in to comment.
Something went wrong with that request. Please try again.