فهرست منبع

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