简体   繁体   中英

Django test client does not handle exceptions?

I've the need to write some tests for custom handler404 and handler500 in Django, using django test dummy client. The first is easy to test while I have problems with the second.

Basically, problem is that Django test client does not catch the exception and does not route to the proper handler. This is a problem because we need to test that the right custom handler and template are used.

We have a simple middleware class to simulate exceptions for tests:

  class HTTPStatusCodeMiddleware(object):
    def process_view(self, request, *args):
        if 'cookie_500_error' in request.COOKIES:
            raise Exception('Test exception')

Code above works fine for manual testing in browser.

Now, tests are:

  def test_404_template(self):                                                 
      c = Client()                                                             
      url = '/not_existing.html'                                               
      response = c.get(url)                                                    
      self.assertEqual(response.status_code, 404)  # success                  
      self.assertTemplateUsed(response, 'custom/404.html')  # success   

  def test_500_template(self):                                                 
      c = Client()                                                             
      c.cookies['cookie_500_error'] = '1'                                  
      response = c.get('/')  # here middleware raises exception!!!
      self.assertEqual(response.status_code, 500)
      self.assertTemplateUsed(response, 'custom/500.html')

Any idea? I don't have the option to use selenium. Thanks!

Django test client handles only some exceptions (see the docs ). All the others are visible in the test and you can test them ;)

This is now supported by Django 3.0

The new 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 exc_info , a tuple providing information of the exception that occurred.

c = Client(raise_request_exception=False)

For information at the documentation

For Django 2.x, the test client can be stopped from raising exceptions by patching its store_exc_info method:

c = Client()                                                             
c.store_exc_info = lambda *args, **kw: None
response = c.get("/error")
assert response.status_code == 500

Assuming that the view handling the /error url raises an exception, this would allow us to get the 500 Server Error response.

For Django>=3.0, the raise_request_exception parameter should be used, instead (as mentioned in @CesarCanassa answer)

So django test Client class doesn't catch exceptions by design, and this is a good thing.

Since the problem specifically was to test custom handler500, which is set by custom middleware on specific settings just by replacing request.urlconf variable with the custom urlconf module, the solution was to use RequestFactory to build the request, and test request.urlconf.handler500 view:

in custom/client1/urls.py

def handler500(request):
    data = {'client', 'client1 is sorry!'}
    ctx = RequestContext(request)
    content = render_to_string('client1/500.html', data, ctx)
    return http.HttpResponseServerError(content)

in tests/views/test_error_pages.py

def test_500_template(self):
    req = RequestFactory().get('/')
    req.user = AnonymousUser()
    req.session = {}

    cust_mw = CustomUrlconfMiddleware()
    cust_mw.process_request(req)  # set the custom request.urlconf
    urlconf = importlib.import_module(req.urlconf)
    response = urlconf.handler500(req)
    self.assertEqual(response.status_code, 500)
    assert_str = '/static/img/custom/client1/'
    self.assertTrue(assert_str in response.content,
                    msg='500 template is not for client1. String {} is not in content'.format(assert_str))

Okay I just ran into a similar problem with using PermissionDenied exceptions in my unit tests. Since Django doesn't catch them, it was causing my test to crash instead of fail. I did figure out an easy solution that I haven't seen posted yet and it's this:

Sample view:

from django.core.exceptions import PermissionDenied

class SampleView(object):
    def setup(request):
        if not request_is_authenticated(request):
            raise PermissionDenied

Unit test:

from django.core.exceptions import PermissionDenied

def test_permission(self):
    try:
        SampleView.setup(request)
    except PermissionDenied:
        print('Hallelujah no crashing!')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM