Fixed #23606 -- Implemented Client and RequestFactory trace() methods.
Thanks KevinEtienne for the suggestion.
This commit is contained in:
parent
713f23492a
commit
28634394f5
|
@ -306,6 +306,10 @@ class RequestFactory(object):
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
return self.generic('HEAD', path, secure=secure, **r)
|
return self.generic('HEAD', path, secure=secure, **r)
|
||||||
|
|
||||||
|
def trace(self, path, secure=False, **extra):
|
||||||
|
"Construct a TRACE request."
|
||||||
|
return self.generic('TRACE', path, secure=secure, **extra)
|
||||||
|
|
||||||
def options(self, path, data='', content_type='application/octet-stream',
|
def options(self, path, data='', content_type='application/octet-stream',
|
||||||
secure=False, **extra):
|
secure=False, **extra):
|
||||||
"Construct an OPTIONS request."
|
"Construct an OPTIONS request."
|
||||||
|
@ -552,6 +556,15 @@ class Client(RequestFactory):
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def trace(self, path, data='', follow=False, secure=False, **extra):
|
||||||
|
"""
|
||||||
|
Send a TRACE request to the server.
|
||||||
|
"""
|
||||||
|
response = super(Client, self).trace(path, data=data, secure=secure, **extra)
|
||||||
|
if follow:
|
||||||
|
response = self._handle_redirects(response, **extra)
|
||||||
|
return response
|
||||||
|
|
||||||
def login(self, **credentials):
|
def login(self, **credentials):
|
||||||
"""
|
"""
|
||||||
Sets the Factory to appear as if it has successfully logged into a site.
|
Sets the Factory to appear as if it has successfully logged into a site.
|
||||||
|
|
|
@ -372,6 +372,10 @@ Requests and Responses
|
||||||
Tests
|
Tests
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
|
* The :class:`RequestFactory.trace() <django.test.RequestFactory>`
|
||||||
|
and :class:`Client.trace() <django.test.Client.trace>` methods were
|
||||||
|
implemented, allowing you to create ``TRACE`` requests in your tests.
|
||||||
|
|
||||||
* The ``count`` argument was added to
|
* The ``count`` argument was added to
|
||||||
:meth:`~django.test.SimpleTestCase.assertTemplateUsed`. This allows you to
|
:meth:`~django.test.SimpleTestCase.assertTemplateUsed`. This allows you to
|
||||||
assert that a template was rendered a specific number of times.
|
assert that a template was rendered a specific number of times.
|
||||||
|
|
|
@ -21,8 +21,8 @@ restricted subset of the test client API:
|
||||||
|
|
||||||
* It only has access to the HTTP methods :meth:`~Client.get()`,
|
* It only has access to the HTTP methods :meth:`~Client.get()`,
|
||||||
:meth:`~Client.post()`, :meth:`~Client.put()`,
|
:meth:`~Client.post()`, :meth:`~Client.put()`,
|
||||||
:meth:`~Client.delete()`, :meth:`~Client.head()` and
|
:meth:`~Client.delete()`, :meth:`~Client.head()`,
|
||||||
:meth:`~Client.options()`.
|
:meth:`~Client.options()`, and :meth:`~Client.trace()`.
|
||||||
|
|
||||||
* These methods accept all the same arguments *except* for
|
* These methods accept all the same arguments *except* for
|
||||||
``follows``. Since this is just a factory for producing
|
``follows``. Since this is just a factory for producing
|
||||||
|
|
|
@ -316,6 +316,20 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
The ``follow``, ``secure`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure`` and ``extra`` arguments act the same as for
|
||||||
:meth:`Client.get`.
|
:meth:`Client.get`.
|
||||||
|
|
||||||
|
.. method:: Client.trace(path, follow=False, secure=False, **extra)
|
||||||
|
|
||||||
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
Makes a TRACE request on the provided ``path`` and returns a
|
||||||
|
``Response`` object. Useful for simulating diagnostic probes.
|
||||||
|
|
||||||
|
Unlike the other request methods, ``data`` is not provided as a keyword
|
||||||
|
parameter in order to comply with :rfc:`2616`, which mandates that
|
||||||
|
TRACE requests should not have an entity-body.
|
||||||
|
|
||||||
|
The ``follow``, ``secure``, and ``extra`` arguments act the same as for
|
||||||
|
:meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.login(**credentials)
|
.. method:: Client.login(**credentials)
|
||||||
|
|
||||||
If your site uses Django's :doc:`authentication system</topics/auth/index>`
|
If your site uses Django's :doc:`authentication system</topics/auth/index>`
|
||||||
|
|
|
@ -23,10 +23,11 @@ rather than the HTML rendered to the end-user.
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
from django.http import HttpResponse
|
||||||
from django.test import Client, TestCase, RequestFactory
|
from django.test import Client, TestCase, RequestFactory
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from .views import get_view
|
from .views import get_view, post_view, trace_view
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||||
|
@ -79,6 +80,13 @@ class ClientTest(TestCase):
|
||||||
self.assertEqual(response.templates[0].name, 'POST Template')
|
self.assertEqual(response.templates[0].name, 'POST Template')
|
||||||
self.assertContains(response, 'Data received')
|
self.assertContains(response, 'Data received')
|
||||||
|
|
||||||
|
def test_trace(self):
|
||||||
|
"""TRACE a view"""
|
||||||
|
response = self.client.trace('/trace_view/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context['method'], 'TRACE')
|
||||||
|
self.assertEqual(response.templates[0].name, 'TRACE Template')
|
||||||
|
|
||||||
def test_response_headers(self):
|
def test_response_headers(self):
|
||||||
"Check the value of HTTP headers returned in a response"
|
"Check the value of HTTP headers returned in a response"
|
||||||
response = self.client.get("/header_view/")
|
response = self.client.get("/header_view/")
|
||||||
|
@ -552,13 +560,54 @@ class CustomTestClientTest(TestCase):
|
||||||
self.assertEqual(hasattr(self.client, "i_am_customized"), True)
|
self.assertEqual(hasattr(self.client, "i_am_customized"), True)
|
||||||
|
|
||||||
|
|
||||||
|
_generic_view = lambda request: HttpResponse(status=200)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='test_client.urls')
|
@override_settings(ROOT_URLCONF='test_client.urls')
|
||||||
class RequestFactoryTest(TestCase):
|
class RequestFactoryTest(TestCase):
|
||||||
|
"""Tests for the request factory."""
|
||||||
|
|
||||||
|
# A mapping between names of HTTP/1.1 methods and their test views.
|
||||||
|
http_methods_and_views = (
|
||||||
|
('get', get_view),
|
||||||
|
('post', post_view),
|
||||||
|
('put', _generic_view),
|
||||||
|
('patch', _generic_view),
|
||||||
|
('delete', _generic_view),
|
||||||
|
('head', _generic_view),
|
||||||
|
('options', _generic_view),
|
||||||
|
('trace', trace_view),
|
||||||
|
)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.request_factory = RequestFactory()
|
||||||
|
|
||||||
def test_request_factory(self):
|
def test_request_factory(self):
|
||||||
factory = RequestFactory()
|
"""The request factory implements all the HTTP/1.1 methods."""
|
||||||
request = factory.get('/somewhere/')
|
for method_name, view in self.http_methods_and_views:
|
||||||
|
method = getattr(self.request_factory, method_name)
|
||||||
|
request = method('/somewhere/')
|
||||||
|
response = view(request)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_get_request_from_factory(self):
|
||||||
|
"""
|
||||||
|
The request factory returns a templated response for a GET request.
|
||||||
|
"""
|
||||||
|
request = self.request_factory.get('/somewhere/')
|
||||||
response = get_view(request)
|
response = get_view(request)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, 'This is a test')
|
self.assertContains(response, 'This is a test')
|
||||||
|
|
||||||
|
def test_trace_request_from_factory(self):
|
||||||
|
"""The request factory returns an echo response for a TRACE request."""
|
||||||
|
url_path = '/somewhere/'
|
||||||
|
request = self.request_factory.trace(url_path)
|
||||||
|
response = trace_view(request)
|
||||||
|
protocol = request.META["SERVER_PROTOCOL"]
|
||||||
|
echoed_request_line = "TRACE {} {}".format(url_path, protocol)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, echoed_request_line)
|
||||||
|
|
|
@ -8,6 +8,7 @@ from . import views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^get_view/$', views.get_view, name='get_view'),
|
url(r'^get_view/$', views.get_view, name='get_view'),
|
||||||
url(r'^post_view/$', views.post_view),
|
url(r'^post_view/$', views.post_view),
|
||||||
|
url(r'^trace_view/$', views.trace_view),
|
||||||
url(r'^header_view/$', views.view_with_header),
|
url(r'^header_view/$', views.view_with_header),
|
||||||
url(r'^raw_post_view/$', views.raw_post_view),
|
url(r'^raw_post_view/$', views.raw_post_view),
|
||||||
url(r'^redirect_view/$', views.redirect_view),
|
url(r'^redirect_view/$', views.redirect_view),
|
||||||
|
|
|
@ -5,7 +5,10 @@ from django.core import mail
|
||||||
from django.forms import fields
|
from django.forms import fields
|
||||||
from django.forms.forms import Form, ValidationError
|
from django.forms.forms import Form, ValidationError
|
||||||
from django.forms.formsets import formset_factory, BaseFormSet
|
from django.forms.formsets import formset_factory, BaseFormSet
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
|
from django.http import (
|
||||||
|
HttpResponse, HttpResponseRedirect, HttpResponseNotFound,
|
||||||
|
HttpResponseNotAllowed, HttpResponseBadRequest,
|
||||||
|
)
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
@ -20,6 +23,31 @@ def get_view(request):
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
|
||||||
|
def trace_view(request):
|
||||||
|
"""
|
||||||
|
A simple view that expects a TRACE request and echoes its status line.
|
||||||
|
|
||||||
|
TRACE requests should not have an entity; the view will return a 400 status
|
||||||
|
response if it is present.
|
||||||
|
"""
|
||||||
|
if request.method.upper() != "TRACE":
|
||||||
|
return HttpResponseNotAllowed("TRACE")
|
||||||
|
elif request.body:
|
||||||
|
return HttpResponseBadRequest("TRACE requests MUST NOT include an entity")
|
||||||
|
else:
|
||||||
|
protocol = request.META["SERVER_PROTOCOL"]
|
||||||
|
t = Template(
|
||||||
|
'{{ method }} {{ uri }} {{ version }}',
|
||||||
|
name="TRACE Template",
|
||||||
|
)
|
||||||
|
c = Context({
|
||||||
|
'method': request.method,
|
||||||
|
'uri': request.path,
|
||||||
|
'version': protocol,
|
||||||
|
})
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
|
||||||
def post_view(request):
|
def post_view(request):
|
||||||
"""A view that expects a POST, and returns a different template depending
|
"""A view that expects a POST, and returns a different template depending
|
||||||
on whether any POST data is available
|
on whether any POST data is available
|
||||||
|
|
Loading…
Reference in New Issue