summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy McKay <amckay@mozilla.com>2015-05-08 22:33:26 -0700
committerTim Graham <timograham@gmail.com>2015-05-25 10:42:30 -0400
commit4525a0c4669dccb3a4c5d865184f8bb8e4a89ec8 (patch)
treef54f254b0043ffbb78e1f7a81f27c705650254c8
parent46ce72e8d2588bf62e4d4e7f08019e7ad2ef0fe6 (diff)
Fixed #24773 -- Added a json() method on test client responses.
-rw-r--r--django/test/client.py8
-rw-r--r--docs/releases/1.9.txt3
-rw-r--r--docs/topics/testing/tools.txt14
-rw-r--r--tests/test_client_regress/tests.py10
-rw-r--r--tests/test_client_regress/urls.py1
-rw-r--r--tests/test_client_regress/views.py6
6 files changed, 40 insertions, 2 deletions
diff --git a/django/test/client.py b/django/test/client.py
index 043bf884a1..f8c2705f98 100644
--- a/django/test/client.py
+++ b/django/test/client.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import json
import mimetypes
import os
import re
@@ -473,6 +474,8 @@ class Client(RequestFactory):
response.templates = data.get("templates", [])
response.context = data.get("context")
+ response.json = curry(self._parse_json, response)
+
# Attach the ResolverMatch instance to the response
response.resolver_match = SimpleLazyObject(
lambda: urlresolvers.resolve(request['PATH_INFO']))
@@ -641,6 +644,11 @@ class Client(RequestFactory):
logout(request)
self.cookies = SimpleCookie()
+ def _parse_json(self, response, **extra):
+ if 'application/json' not in response.get('Content-Type'):
+ raise ValueError('Content-Type header is "{0}", not "application/json"'.format(response.get('Content-Type')))
+ return json.loads(response.content.decode(), **extra)
+
def _handle_redirects(self, response, **extra):
"Follows any redirects by requesting responses from the server using GET."
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index e36c4512ca..5b58f79fc0 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -289,7 +289,8 @@ Requests and Responses
Tests
^^^^^
-* ...
+* Added the :meth:`json() <django.test.Response.json>` method to test client
+ responses to give access to the response body as JSON.
URLs
^^^^
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt
index aa66afb1d9..e2ad15d5b5 100644
--- a/docs/topics/testing/tools.txt
+++ b/docs/topics/testing/tools.txt
@@ -425,6 +425,20 @@ Specifically, a ``Response`` object has the following attributes:
>>> response.context['name']
'Arthur'
+ .. method:: json(**kwargs)
+
+ .. versionadded:: 1.9
+
+ The body of the response, parsed as JSON. Extra keyword arguments are
+ passed to :func:`json.loads`. For example::
+
+ >>> response = client.get('/foo/')
+ >>> response.json()['name']
+ 'Arthur'
+
+ If the ``Content-Type`` header is not ``"application/json"``, then a
+ :exc:`ValueError` will be raised when trying to parse the response.
+
.. attribute:: request
The request data that stimulated the response.
diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py
index f47ae10b56..efc44289cd 100644
--- a/tests/test_client_regress/tests.py
+++ b/tests/test_client_regress/tests.py
@@ -1270,6 +1270,16 @@ class RequestMethodStringDataTests(SimpleTestCase):
response = self.client.head('/body/', data='', content_type='application/json')
self.assertEqual(response.content, b'')
+ def test_json(self):
+ response = self.client.get('/json_response/')
+ self.assertEqual(response.json(), {'key': 'value'})
+
+ def test_json_wrong_header(self):
+ response = self.client.get('/body/')
+ msg = 'Content-Type header is "text/html; charset=utf-8", not "application/json"'
+ with self.assertRaisesMessage(ValueError, msg):
+ self.assertEqual(response.json(), {'key': 'value'})
+
@override_settings(ROOT_URLCONF='test_client_regress.urls',)
class QueryStringTests(SimpleTestCase):
diff --git a/tests/test_client_regress/urls.py b/tests/test_client_regress/urls.py
index fd7c131797..9821ea910d 100644
--- a/tests/test_client_regress/urls.py
+++ b/tests/test_client_regress/urls.py
@@ -30,6 +30,7 @@ urlpatterns = [
url(r'^request_methods/$', views.request_methods_view),
url(r'^check_unicode/$', views.return_unicode),
url(r'^check_binary/$', views.return_undecodable_binary),
+ url(r'^json_response/$', views.return_json_response),
url(r'^parse_unicode_json/$', views.return_json_file),
url(r'^check_headers/$', views.check_headers),
url(r'^check_headers_redirect/$', RedirectView.as_view(url='/check_headers/')),
diff --git a/tests/test_client_regress/views.py b/tests/test_client_regress/views.py
index 6046564ee4..d21448cba1 100644
--- a/tests/test_client_regress/views.py
+++ b/tests/test_client_regress/views.py
@@ -3,7 +3,7 @@ import json
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.serializers.json import DjangoJSONEncoder
-from django.http import HttpResponse, HttpResponseRedirect
+from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.template.loader import render_to_string
@@ -108,6 +108,10 @@ def return_undecodable_binary(request):
)
+def return_json_response(request):
+ return JsonResponse({'key': 'value'})
+
+
def return_json_file(request):
"A view that parses and returns a JSON string as a file."
match = CONTENT_TYPE_RE.match(request.META['CONTENT_TYPE'])