Index: django/test/client.py
===================================================================
--- django/test/client.py	(revision 4223)
+++ django/test/client.py	(working copy)
@@ -1,6 +1,8 @@
 from cStringIO import StringIO
+import sys
 from django.core.handlers.base import BaseHandler
 from django.core.handlers.wsgi import WSGIRequest
+from django.core.signals import got_request_exception
 from django.dispatch import dispatcher
 from django.http import urlencode, SimpleCookie
 from django.test import signals
@@ -98,6 +100,7 @@
         self.handler = ClientHandler()
         self.defaults = defaults
         self.cookie = SimpleCookie()
+        self.exc_info = None
 
     def request(self, **request):
         """
@@ -106,6 +109,7 @@
         Assumes defaults for the query environment, which can be overridden
         using the arguments to the request.
         """
+        self.exc_info = None
 
         environ = {
             'HTTP_COOKIE':      self.cookie,
@@ -126,6 +130,9 @@
         on_template_render = curry(store_rendered_templates, data)
         dispatcher.connect(on_template_render, signal=signals.template_rendered)
 
+        # Capture exceptions created by the handler
+        dispatcher.connect(self.store_exc_info, signal=got_request_exception)
+
         response = self.handler(environ)
 
         # Add any rendered template detail to the response
@@ -143,6 +150,10 @@
         if response.cookies:
             self.cookie.update(response.cookies)
 
+        # Look for a signalled exception and reraise it
+        if self.exc_info:
+            raise self.exc_info[1], None, self.exc_info[2]
+
         return response
 
     def get(self, path, data={}, **extra):
@@ -214,3 +241,6 @@
 
         # Since we are logged in, request the actual page again
         return self.get(path)
+
+    def store_exc_info(self, *args, **kwargs):
+        self.exc_info = sys.exc_info()
Index: django/core/handlers/base.py
===================================================================
--- django/core/handlers/base.py	(revision 4223)
+++ django/core/handlers/base.py	(working copy)
@@ -103,13 +103,13 @@
         except SystemExit:
             pass # See http://code.djangoproject.com/ticket/1023
         except: # Handle everything else, including SuspiciousOperation, etc.
+            receivers = dispatcher.send(signal=signals.got_request_exception)
             if settings.DEBUG:
                 from django.views import debug
                 return debug.technical_500_response(request, *sys.exc_info())
             else:
                 # Get the exception info now, in case another exception is thrown later.
                 exc_info = sys.exc_info()
-                receivers = dispatcher.send(signal=signals.got_request_exception)
                 # When DEBUG is False, send an error message to the admins.
                 subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)
                 try:
Index: tests/modeltests/test_client/views.py
===================================================================
--- tests/modeltests/test_client/views.py	(revision 4223)
+++ tests/modeltests/test_client/views.py	(working copy)
@@ -33,3 +49,7 @@
     c = Context({'user': request.user})
     
     return HttpResponse(t.render(c))
+
+def broken_view(request):
+    """A view which just raises an exception, simulating a broken view."""
+    raise KeyError("Oops! Looks like you wrote some bad code.")
Index: tests/modeltests/test_client/models.py
===================================================================
--- tests/modeltests/test_client/models.py	(revision 4223)
+++ tests/modeltests/test_client/models.py	(working copy)
@@ -99,3 +107,6 @@
 
         response = self.client.login('/test_client/login_protected_view/', 'otheruser', 'nopassword')
         self.assertFalse(response)
+
+    def test_view_with_exception(self):
+        self.assertRaises(KeyError, self.client.get, "/test_client/broken_view/")
Index: tests/modeltests/test_client/urls.py
===================================================================
--- tests/modeltests/test_client/urls.py        (revision 4223)
+++ tests/modeltests/test_client/urls.py        (working copy)
@@ -6,4 +6,5 @@
     (r'^post_view/$', views.post_view),
     (r'^redirect_view/$', views.redirect_view),
     (r'^login_protected_view/$', views.login_protected_view),
+    (r'^broken_view/$', views.broken_view),
 )
Index: docs/testing.txt
===================================================================
--- docs/testing.txt	(revision 4223)
+++ docs/testing.txt	(working copy)
@@ -314,6 +324,21 @@
             # Check that the rendered context contains 5 customers
             self.failUnlessEqual(len(response.context['customers']), 5)
 
+Exceptions
+----------
+
+Exceptions raised by views or other code while handling a request made by
+``Client`` will bubble up to your test case. The default test runner,
+``django.test.simple.run_tests`` will print a stack trace for an unhandled
+exception. ``unittest.TestCase``'s ``assertRaises`` method can be used to test
+that exceptions are raised by views, which can be useful to test error
+handling. You may also catch exceptions in the normal way and inspect them in
+your test code.
+
+``Http404``, ``PermissionDenied`` and ``SystemExit`` are caught by Django's
+base handler. These exceptions will not make it back to your test if raised
+during a request.
+
 Fixtures
 --------
 
