浏览代码

Fixed #29687 -- Allowed the test client to serialize list/tuple as JSON.

Dan Palmer 6 年之前
父节点
当前提交
e181666973
共有 6 个文件被更改,包括 33 次插入15 次删除
  1. 1 0
      AUTHORS
  2. 3 3
      django/test/client.py
  3. 4 0
      docs/releases/2.2.txt
  4. 10 4
      docs/topics/testing/tools.txt
  5. 13 6
      tests/test_client/tests.py
  6. 2 2
      tests/test_client/views.py

+ 1 - 0
AUTHORS

@@ -206,6 +206,7 @@ answer newbie questions, and generally made Django that much better:
     Daniel Wiesmann <daniel.wiesmann@gmail.com>
     Danilo Bargen
     Dan Johnson <danj.py@gmail.com>
+    Dan Palmer <dan@danpalmer.me>
     Dan Poirier <poirier@pobox.com>
     Dan Stephenson <http://dan.io/>
     Dan Watson <http://danwatson.net/>

+ 3 - 3
django/test/client.py

@@ -317,10 +317,10 @@ class RequestFactory:
 
     def _encode_json(self, data, content_type):
         """
-        Return encoded JSON if data is a dict and content_type is
-        application/json.
+        Return encoded JSON if data is a dict, list, or tuple and content_type
+        is application/json.
         """
-        should_encode = JSON_CONTENT_TYPE_RE.match(content_type) and isinstance(data, dict)
+        should_encode = JSON_CONTENT_TYPE_RE.match(content_type) and isinstance(data, (dict, list, tuple))
         return json.dumps(data, cls=self.json_encoder) if should_encode else data
 
     def _get_path(self, parsed):

+ 4 - 0
docs/releases/2.2.txt

@@ -224,6 +224,10 @@ Tests
   URL, ignoring the ordering of the query string.
   :meth:`~.SimpleTestCase.assertRedirects` uses the new assertion.
 
+* The test :class:`~.django.test.Client` now supports automatic JSON
+  serialization of list and tuple ``data`` when
+  ``content_type='application/json'``.
+
 URLs
 ~~~~
 

+ 10 - 4
docs/topics/testing/tools.txt

@@ -213,10 +213,11 @@ Use the ``django.test.Client`` class to make requests.
 
             name=fred&passwd=secret
 
-        If you provide ``content_type`` as :mimetype:`application/json`, a
-        ``data`` dictionary is serialized using :func:`json.dumps` with
-        :class:`~django.core.serializers.json.DjangoJSONEncoder`. You can
-        change the encoder by providing a ``json_encoder`` argument to
+        If you provide ``content_type`` as :mimetype:`application/json`, the
+        ``data`` is serialized using :func:`json.dumps` if it's a dict, list,
+        or tuple. Serialization is performed with
+        :class:`~django.core.serializers.json.DjangoJSONEncoder` by default,
+        and can be overridden by providing a ``json_encoder`` argument to
         :class:`Client`. This serialization also happens for :meth:`put`,
         :meth:`patch`, and :meth:`delete` requests.
 
@@ -226,6 +227,11 @@ Use the ``django.test.Client`` class to make requests.
             you can call :func:`json.dumps` on ``data`` before passing it to
             ``post()`` to achieve the same thing.
 
+        .. versionchanged:: 2.2
+
+            The JSON serialization was extended to support lists and tuples. In
+            older versions, only dicts are serialized.
+
         If you provide any other ``content_type`` (e.g. :mimetype:`text/xml`
         for an XML payload), the contents of ``data`` are sent as-is in the
         POST request, using ``content_type`` in the HTTP ``Content-Type``

+ 13 - 6
tests/test_client/tests.py

@@ -95,14 +95,21 @@ class ClientTest(TestCase):
     def test_json_serialization(self):
         """The test client serializes JSON data."""
         methods = ('post', 'put', 'patch', 'delete')
+        tests = (
+            ({'value': 37}, {'value': 37}),
+            ([37, True], [37, True]),
+            ((37, False), [37, False]),
+        )
         for method in methods:
             with self.subTest(method=method):
-                client_method = getattr(self.client, method)
-                method_name = method.upper()
-                response = client_method('/json_view/', {'value': 37}, content_type='application/json')
-                self.assertEqual(response.status_code, 200)
-                self.assertEqual(response.context['data'], 37)
-                self.assertContains(response, 'Viewing %s page.' % method_name)
+                for data, expected in tests:
+                    with self.subTest(data):
+                        client_method = getattr(self.client, method)
+                        method_name = method.upper()
+                        response = client_method('/json_view/', data, content_type='application/json')
+                        self.assertEqual(response.status_code, 200)
+                        self.assertEqual(response.context['data'], expected)
+                        self.assertContains(response, 'Viewing %s page.' % method_name)
 
     def test_json_encoder_argument(self):
         """The test Client accepts a json_encoder."""

+ 2 - 2
tests/test_client/views.py

@@ -83,14 +83,14 @@ def post_view(request):
 def json_view(request):
     """
     A view that expects a request with the header 'application/json' and JSON
-    data with a key named 'value'.
+    data, which is deserialized and included in the context.
     """
     if request.META.get('CONTENT_TYPE') != 'application/json':
         return HttpResponse()
 
     t = Template('Viewing {} page. With data {{ data }}.'.format(request.method))
     data = json.loads(request.body.decode('utf-8'))
-    c = Context({'data': data['value']})
+    c = Context({'data': data})
     return HttpResponse(t.render(c))