diff --git a/django/test/client.py b/django/test/client.py index be2c3c8136..b6bf01c1cb 100644 --- a/django/test/client.py +++ b/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 diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 7218bcec22..2e596b3dc8 100644 --- a/docs/releases/1.7.txt +++ b/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 ===================================== diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 7735e2ab71..7093138188 100644 --- a/docs/topics/testing/overview.txt +++ b/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) diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index 38a0f38408..a01d1f3845 100644 --- a/tests/test_client/tests.py +++ b/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/') diff --git a/tests/test_client/views.py b/tests/test_client/views.py index 1861e3a591..85aefd47bb 100644 --- a/tests/test_client/views.py +++ b/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):