summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Dufresne <jon.dufresne@gmail.com>2019-02-20 03:16:10 -0800
committerCarlton Gibson <carlton.gibson@noumenal.es>2019-02-20 12:16:10 +0100
commit7feddd878cd0d4ad6cc98f0da2b597d603a211a7 (patch)
treec46e8a85e07348989f3afd0103123ab09dc848fa
parent7071f8f2729295b0da77b6c651966dc32c71f1ab (diff)
Fixed #18707 -- Added support for the test client to return 500 responses.
-rw-r--r--django/test/client.py7
-rw-r--r--docs/releases/3.0.txt8
-rw-r--r--docs/topics/testing/tools.txt37
-rw-r--r--tests/test_client/tests.py14
4 files changed, 60 insertions, 6 deletions
diff --git a/django/test/client.py b/django/test/client.py
index afc803789e..c8d8508272 100644
--- a/django/test/client.py
+++ b/django/test/client.py
@@ -440,9 +440,10 @@ class Client(RequestFactory):
contexts and templates produced by a view, rather than the
HTML rendered to the end-user.
"""
- def __init__(self, enforce_csrf_checks=False, **defaults):
+ def __init__(self, enforce_csrf_checks=False, raise_request_exception=True, **defaults):
super().__init__(**defaults)
self.handler = ClientHandler(enforce_csrf_checks)
+ self.raise_request_exception = raise_request_exception
self.exc_info = None
def store_exc_info(self, **kwargs):
@@ -497,10 +498,12 @@ class Client(RequestFactory):
# exception data, then re-raise the signalled exception.
# Also make sure that the signalled exception is cleared from
# the local cache!
+ response.exc_info = self.exc_info
if self.exc_info:
_, exc_value, _ = self.exc_info
self.exc_info = None
- raise exc_value
+ if self.raise_request_exception:
+ raise exc_value
# Save the client and request that stimulated the response.
response.client = self
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index f036969c92..77bace661b 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -187,7 +187,13 @@ Templates
Tests
~~~~~
-* ...
+* The new test :class:`~django.test.Client` argument
+ ``raise_request_exception`` allows controlling whether or not exceptions
+ raised during the request should also be raised in the test. The value
+ defaults to ``True`` for backwards compatibility. If it is ``False`` and an
+ exception occurs, the test client will return a 500 response with the
+ attribute :attr:`~django.test.Response.exc_info`, a tuple providing
+ information of the exception that occurred.
URLs
~~~~
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt
index 4ffe84dd04..ea01bd026b 100644
--- a/docs/topics/testing/tools.txt
+++ b/docs/topics/testing/tools.txt
@@ -128,6 +128,14 @@ Use the ``django.test.Client`` class to make requests.
The ``json_encoder`` argument allows setting a custom JSON encoder for
the JSON serialization that's described in :meth:`post`.
+ The ``raise_request_exception`` argument allows controlling whether or not
+ exceptions raised during the request should also be raised in the test.
+ Defaults to ``True``.
+
+ .. versionadded:: 3.0
+
+ The ``raise_request_exception`` argument was added.
+
Once you have a ``Client`` instance, you can call any of the following
methods:
@@ -476,6 +484,23 @@ Specifically, a ``Response`` object has the following attributes:
:attr:`~django.template.response.SimpleTemplateResponse.context_data`
may be a suitable alternative on responses with that attribute.
+ .. attribute:: exc_info
+
+ .. versionadded:: 3.0
+
+ A tuple of three values that provides information about the unhandled
+ exception, if any, that occurred during the view.
+
+ The values are (type, value, traceback), the same as returned by
+ Python's :func:`sys.exc_info`. Their meanings are:
+
+ - *type*: The type of the exception.
+ - *value*: The exception instance.
+ - *traceback*: A traceback object which encapsulates the call stack at
+ the point where the exception originally occurred.
+
+ If no exception occurred, then ``exc_info`` will be ``None``.
+
.. method:: json(**kwargs)
The body of the response, parsed as JSON. Extra keyword arguments are
@@ -544,9 +569,10 @@ content type of a response using ``response['Content-Type']``.
Exceptions
----------
-If you point the test client at a view that raises an exception, that exception
-will be visible in the test case. You can then use a standard ``try ... except``
-block or :meth:`~unittest.TestCase.assertRaises` to test for exceptions.
+If you point the test client at a view that raises an exception and
+``Client.raise_request_exception`` is ``True``, that exception will be visible
+in the test case. You can then use a standard ``try ... except`` block or
+:meth:`~unittest.TestCase.assertRaises` to test for exceptions.
The only exceptions that are not visible to the test client are
:class:`~django.http.Http404`,
@@ -555,6 +581,11 @@ The only exceptions that are not visible to the test client are
exceptions internally and converts them into the appropriate HTTP response
codes. In these cases, you can check ``response.status_code`` in your test.
+If ``Client.raise_request_exception`` is ``False``, the test client will return a
+500 response as would be returned to a browser. The response has the attribute
+:attr:`~Response.exc_info` to provide information about the unhandled
+exception.
+
Persistent state
----------------
diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py
index 432865328f..02f5113890 100644
--- a/tests/test_client/tests.py
+++ b/tests/test_client/tests.py
@@ -759,6 +759,20 @@ class ClientTest(TestCase):
with self.assertRaises(KeyError):
self.client.get("/broken_view/")
+ def test_exc_info(self):
+ client = Client(raise_request_exception=False)
+ response = client.get("/broken_view/")
+ self.assertEqual(response.status_code, 500)
+ exc_type, exc_value, exc_traceback = response.exc_info
+ self.assertIs(exc_type, KeyError)
+ self.assertIsInstance(exc_value, KeyError)
+ self.assertEqual(str(exc_value), "'Oops! Looks like you wrote some bad code.'")
+ self.assertIsNotNone(exc_traceback)
+
+ def test_exc_info_none(self):
+ response = self.client.get("/get_view/")
+ self.assertIsNone(response.exc_info)
+
def test_mail_sending(self):
"Mail is redirected to a dummy outbox during test setup"
response = self.client.get('/mail_sending_view/')