浏览代码

Fixed #21341 -- Eased https requests with the test client

All request methods of ``django.test.client.Client`` receive a ``secure``
argument that defaults to ``False`` indicating whether or not to make the
request through https.
Thanks Aymeric Augustin for the review.
Unai Zalakain 11 年之前
父节点
当前提交
99b681e227
共有 5 个文件被更改,包括 89 次插入45 次删除
  1. 50 32
      django/test/client.py
  2. 4 0
      docs/releases/1.7.txt
  3. 22 13
      docs/topics/testing/overview.txt
  4. 12 0
      tests/test_client/tests.py
  5. 1 0
      tests/test_client/views.py

+ 50 - 32
django/test/client.py

@@ -269,60 +269,68 @@ class RequestFactory(object):
             path = path.encode('utf-8').decode('iso-8859-1')
         return path
 
-    def get(self, path, data={}, **extra):
+    def get(self, path, data={}, secure=False, **extra):
         "Construct a GET request."
 
         r = {
             'QUERY_STRING': urlencode(data, doseq=True),
         }
         r.update(extra)
-        return self.generic('GET', path, **r)
+        return self.generic('GET', path, secure=secure, **r)
 
     def post(self, path, data={}, content_type=MULTIPART_CONTENT,
-             **extra):
+             secure=False, **extra):
         "Construct a POST request."
 
         post_data = self._encode_data(data, content_type)
 
-        return self.generic('POST', path, post_data, content_type, **extra)
+        return self.generic('POST', path, post_data, content_type,
+                            secure=secure, **extra)
 
-    def head(self, path, data={}, **extra):
+    def head(self, path, data={}, secure=False, **extra):
         "Construct a HEAD request."
 
         r = {
             'QUERY_STRING': urlencode(data, doseq=True),
         }
         r.update(extra)
-        return self.generic('HEAD', path, **r)
+        return self.generic('HEAD', path, secure=secure, **r)
 
     def options(self, path, data='', content_type='application/octet-stream',
-                **extra):
+                secure=False, **extra):
         "Construct an OPTIONS request."
-        return self.generic('OPTIONS', path, data, content_type, **extra)
+        return self.generic('OPTIONS', path, data, content_type,
+                            secure=secure, **extra)
 
     def put(self, path, data='', content_type='application/octet-stream',
-            **extra):
+            secure=False, **extra):
         "Construct a PUT request."
-        return self.generic('PUT', path, data, content_type, **extra)
+        return self.generic('PUT', path, data, content_type,
+                            secure=secure, **extra)
 
     def patch(self, path, data='', content_type='application/octet-stream',
-              **extra):
+              secure=False, **extra):
         "Construct a PATCH request."
-        return self.generic('PATCH', path, data, content_type, **extra)
+        return self.generic('PATCH', path, data, content_type,
+                            secure=secure, **extra)
 
     def delete(self, path, data='', content_type='application/octet-stream',
-               **extra):
+               secure=False, **extra):
         "Construct a DELETE request."
-        return self.generic('DELETE', path, data, content_type, **extra)
+        return self.generic('DELETE', path, data, content_type,
+                            secure=secure, **extra)
 
-    def generic(self, method, path,
-                data='', content_type='application/octet-stream', **extra):
+    def generic(self, method, path, data='',
+                content_type='application/octet-stream', secure=False,
+                **extra):
         """Constructs an arbitrary HTTP request."""
         parsed = urlparse(path)
         data = force_bytes(data, settings.DEFAULT_CHARSET)
         r = {
             'PATH_INFO':      self._get_path(parsed),
             'REQUEST_METHOD': str(method),
+            'SERVER_PORT': str('443') if secure else str('80'),
+            'wsgi.url_scheme': str('https') if secure else str('http'),
         }
         if data:
             r.update({
@@ -445,72 +453,82 @@ class Client(RequestFactory):
             signals.template_rendered.disconnect(dispatch_uid=signal_uid)
             got_request_exception.disconnect(dispatch_uid="request-exception")
 
-    def get(self, path, data={}, follow=False, **extra):
+    def get(self, path, data={}, follow=False, secure=False, **extra):
         """
         Requests a response from the server using GET.
         """
-        response = super(Client, self).get(path, data=data, **extra)
+        response = super(Client, self).get(path, data=data, secure=secure,
+                                           **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response
 
     def post(self, path, data={}, content_type=MULTIPART_CONTENT,
-             follow=False, **extra):
+             follow=False, secure=False, **extra):
         """
         Requests a response from the server using POST.
         """
-        response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
+        response = super(Client, self).post(path, data=data,
+                                            content_type=content_type,
+                                            secure=secure, **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response
 
-    def head(self, path, data={}, follow=False, **extra):
+    def head(self, path, data={}, follow=False, secure=False, **extra):
         """
         Request a response from the server using HEAD.
         """
-        response = super(Client, self).head(path, data=data, **extra)
+        response = super(Client, self).head(path, data=data, secure=secure,
+                                            **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response
 
     def options(self, path, data='', content_type='application/octet-stream',
-                follow=False, **extra):
+                follow=False, secure=False, **extra):
         """
         Request a response from the server using OPTIONS.
         """
-        response = super(Client, self).options(path, data=data, content_type=content_type, **extra)
+        response = super(Client, self).options(path, data=data,
+                                               content_type=content_type,
+                                               secure=secure, **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response
 
     def put(self, path, data='', content_type='application/octet-stream',
-            follow=False, **extra):
+            follow=False, secure=False, **extra):
         """
         Send a resource to the server using PUT.
         """
-        response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
+        response = super(Client, self).put(path, data=data,
+                                           content_type=content_type,
+                                           secure=secure, **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response
 
     def patch(self, path, data='', content_type='application/octet-stream',
-              follow=False, **extra):
+              follow=False, secure=False, **extra):
         """
         Send a resource to the server using PATCH.
         """
-        response = super(Client, self).patch(
-            path, data=data, content_type=content_type, **extra)
+        response = super(Client, self).patch(path, data=data,
+                                             content_type=content_type,
+                                             secure=secure, **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response
 
     def delete(self, path, data='', content_type='application/octet-stream',
-               follow=False, **extra):
+               follow=False, secure=False, **extra):
         """
         Send a DELETE request to the server.
         """
-        response = super(Client, self).delete(
-            path, data=data, content_type=content_type, **extra)
+        response = super(Client, self).delete(path, data=data,
+                                              content_type=content_type,
+                                              secure=secure, **extra)
         if follow:
             response = self._handle_redirects(response, **extra)
         return response

+ 4 - 0
docs/releases/1.7.txt

@@ -444,6 +444,10 @@ Tests
   client can't fetch externals URLs, this allows you to use ``assertRedirects``
   with redirects that aren't part of your Django app.
 
+* The ``secure`` argument was added to all the request methods of
+  :class:`~django.test.Client`. If ``True``, the request will be made
+  through HTTPS.
+
 Backwards incompatible changes in 1.7
 =====================================
 

+ 22 - 13
docs/topics/testing/overview.txt

@@ -431,8 +431,11 @@ Use the ``django.test.Client`` class to make requests.
     Once you have a ``Client`` instance, you can call any of the following
     methods:
 
-    .. method:: Client.get(path, data={}, follow=False, **extra)
+    .. method:: Client.get(path, data={}, follow=False, secure=False, **extra)
 
+        .. versionadded:: 1.7
+
+            The ``secure`` argument was added.
 
         Makes a GET request on the provided ``path`` and returns a ``Response``
         object, which is documented below.
@@ -488,7 +491,10 @@ Use the ``django.test.Client`` class to make requests.
             >>> response.redirect_chain
             [(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)]
 
-    .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
+        If you set ``secure`` to ``True`` the client will emulate an HTTPS
+        request.
+
+    .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
 
         Makes a POST request on the provided ``path`` and returns a
         ``Response`` object, which is documented below.
@@ -562,14 +568,17 @@ Use the ``django.test.Client`` class to make requests.
         and a ``redirect_chain`` attribute will be set in the response object
         containing tuples of the intermediate urls and status codes.
 
-    .. method:: Client.head(path, data={}, follow=False, **extra)
+        If you set ``secure`` to ``True`` the client will emulate an HTTPS
+        request.
+
+    .. method:: Client.head(path, data={}, follow=False, secure=False, **extra)
 
         Makes a HEAD request on the provided ``path`` and returns a
         ``Response`` object. This method works just like :meth:`Client.get`,
-        including the ``follow`` and ``extra`` arguments, except it does not
-        return a message body.
+        including the ``follow``, ``secure`` and ``extra`` arguments, except
+        it does not return a message body.
 
-    .. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, **extra)
+    .. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
 
         Makes an OPTIONS request on the provided ``path`` and returns a
         ``Response`` object. Useful for testing RESTful interfaces.
@@ -577,10 +586,10 @@ Use the ``django.test.Client`` class to make requests.
         When ``data`` is provided, it is used as the request body, and
         a ``Content-Type`` header is set to ``content_type``.
 
-        The ``follow`` and ``extra`` arguments act the same as for
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
         :meth:`Client.get`.
 
-    .. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, **extra)
+    .. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
 
         Makes a PUT request on the provided ``path`` and returns a
         ``Response`` object. Useful for testing RESTful interfaces.
@@ -588,18 +597,18 @@ Use the ``django.test.Client`` class to make requests.
         When ``data`` is provided, it is used as the request body, and
         a ``Content-Type`` header is set to ``content_type``.
 
-        The ``follow`` and ``extra`` arguments act the same as for
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
         :meth:`Client.get`.
 
-    .. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, **extra)
+    .. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
 
         Makes a PATCH request on the provided ``path`` and returns a
         ``Response`` object. Useful for testing RESTful interfaces.
 
-        The ``follow`` and ``extra`` arguments act the same as for
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
         :meth:`Client.get`.
 
-    .. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, **extra)
+    .. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
 
         Makes an DELETE request on the provided ``path`` and returns a
         ``Response`` object. Useful for testing RESTful interfaces.
@@ -607,7 +616,7 @@ Use the ``django.test.Client`` class to make requests.
         When ``data`` is provided, it is used as the request body, and
         a ``Content-Type`` header is set to ``content_type``.
 
-        The ``follow`` and ``extra`` arguments act the same as for
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
         :meth:`Client.get`.
 
     .. method:: Client.login(**credentials)

+ 12 - 0
tests/test_client/tests.py

@@ -93,6 +93,18 @@ class ClientTest(TestCase):
         self.assertEqual(response.templates[0].name, "Book template")
         self.assertEqual(response.content, b"Blink - Malcolm Gladwell")
 
+    def test_insecure(self):
+        "GET a URL through http"
+        response = self.client.get('/test_client/secure_view/', secure=False)
+        self.assertFalse(response.test_was_secure_request)
+        self.assertEqual(response.test_server_port, '80')
+
+    def test_secure(self):
+        "GET a URL through https"
+        response = self.client.get('/test_client/secure_view/', secure=True)
+        self.assertTrue(response.test_was_secure_request)
+        self.assertEqual(response.test_server_port, '443')
+
     def test_redirect(self):
         "GET a URL that redirects elsewhere"
         response = self.client.get('/test_client/redirect_view/')

+ 1 - 0
tests/test_client/views.py

@@ -68,6 +68,7 @@ def view_with_secure(request):
     "A view that indicates if the request was secure"
     response = HttpResponse()
     response.test_was_secure_request = request.is_secure()
+    response.test_server_port = request.META.get('SERVER_PORT', 80)
     return response
 
 def double_redirect_view(request):