Testing App Views
First of all, thanks to those people who've been reading and pointing out improvements in the comments - it's much appreciated, and I've learned a lot from you!
Django applications tend to have views, in a views.py file. These are generally looked up by URL. URLConfs (various urls.py files) are used to map request URLs to views.
This can lead to a problem from a testing perspective. Not all applications have urls.py files; and for those that do, there's nothing stopping a project wiring up different URLs to those views through a custom urls.py. It therefore becomes difficult to use Django's test client to invoke a view using a GET or a POST because you don't know how that URL has been configured. After all, you run python manage.py test from your project, and the project's configuration is used.
Let's say I have a views.py that looks like this:
from django.shortcuts import render_to_response
from django.template import RequestContext
def say_hi(request):
return render_to_response('app/hi.html', context=RequestContext(request))
I want to use Django's test client to ensure that when this view is called, it returns an HTTP 200 OK response. However, my app doesn't have a urls.py - the person using it is meant to wire this view into whatever URL they want to. How do I test the view?
The solution is deceptively simple: inject a test URLs module.
Create a base test case that looks something like this:
from django.conf import settings
from django.test import TestCase
class TestUrlsTestCase(TestCase):
def setUp(self):
self._old_root_urlconf = settings.ROOT_URLCONF
settings.ROOT_URLCONF = 'app.testurls'
def tearDown(self):
self.ROOT_URLCONF = self._old_root_urlconf
Remember that setUp() is run just before each test, and tearDown() is run just after. What we're doing is replacing the URLConf just for the duration of the test. We have to put it back in the tearDown(), else we'll end up with test state 'leakage' into other tests.
You then just have to add a testurls.py file to the 'app' application (the one I'm testing) which contains known URLs for the views I want to invoke. For example, my testurls.py might look like this:
from django.conf.urls.defaults import *
urlpatterns = patterns('app.views',
(r'^foo/', 'say_hi'),
)
I can then write standard test client code to check that my views are working as expected:
class RealTest(TestUrlsTestCase):
def testGet(self):
response = self.client.get('/foo/')
self.assertEqual(200, response.status_code)
It now doesn't matter how the project integrator has configured their urls.py. When the tests are run, the URLConf that I have specified will always be used.