Testing for expected 404 with Django test client leads to unhandled exception
Asked Answered
R

2

7

I'm using the Django test client, django.test.client.Client, to test some views in a Django app. In particular, I'm testing a case where the view calls a get_object_or_404 method and the object isn't there, so a 404 should be returned.

My test code looks like this:

class ViewTests(TestCase):
    fixtures=['test.json']

    def test_thing_not_there_get(self):
        url = '/foo/30afda98-b9d7-4e26-a59a-76ac1b6a001f/'
        c = django.test.client.Client()
        response = c.get(url)
        self.assertEqual(response.status_code, 404)

However, what I'm getting instead is an unhandled exception error in the view code:

python projects/unittests/manage.py test 
Creating test database for alias 'default'...
......ERROR:root:Unhandled Exception on request for http://testserver/foo/30afda98-b9d7-4e26-a59a-76ac1b6a001f/
Traceback (most recent call last):
  File "/Users/lorin/.virtualenvs/myvenv/lib/python2.7/site-packages/django/core/handlers/base.py", line 111, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/Users/lorin/.virtualenvs/myvenv/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 39, in wrapped_view
    resp = view_func(*args, **kwargs)
  File "/Users/lorin/.virtualenvs/myvenv/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 52, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/lorin/django-myvenv/apps/myvenv_desktop/views.py", line 85, in foo_view
    instance = get_object_or_404(Foo, uuid=foo_uuid)
  File "/Users/lorin/.virtualenvs/myvenv/lib/python2.7/site-packages/django/shortcuts/__init__.py", line 115, in get_object_or_404
    raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
Http404: No Foo matches the given query.

According to the Django 1.3 docs

The only exceptions that are not visible to the test client are Http404, PermissionDenied and SystemExit. Django catches these exceptions internally and converts them into the appropriate HTTP response codes. In these cases, you can check response.status_code in your test.

Why isn't Django catching the Http404 exception in this case?

Note that (in conformance with the docs), the exception is not being thrown across into the test client. If I try to catch the exception on the client side:

with self.assertRaises(django.http.Http404):
    response = c.get(url)

I get the same error, as well as an additional error:

AssertionError: Http404 not raised
Readership answered 22/1, 2012 at 12:36 Comment(1)
But, according to your answer, django must return a 404 response code, it's a django error, not?Shainashaine
K
6

I know this is from a while ago, but I solved this so I figured it's worth sharing. What you need to do is customize the error handler in your code. For example, I have:

in urls.py:

from myapp.views import error_handler
handler404 = error_handler.error404

and the function error404:

from django.http import HttpResponseNotFound
def error404(request):
    t = loader.get_template('404.html')
    html = t.render(Context())

    return HttpResponseNotFound(html)

If you send a HttpResponseNotFound object, it will interpret as a 404 status code.

EDIT

In newer versions of django its ok just to put a 404.html in the templates directory. The above controller is only necessary if you need more logic than just showing the error page.

Koziarz answered 21/2, 2013 at 15:39 Comment(0)
R
2

Upon further inspection, it looks like the test is passing as written: the error message doesn't actually correspond to a test failure.

Readership answered 22/1, 2012 at 12:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.