Testing a Django Application's View
In our previous article, we learned how to write automated tests for our Django application, which involves writing a simple test to verify the behaviour of the model method m.Post.recent_posts()
and fixing the bug where the method recent_posts()
returns future posts.
In this article, we are going to learn how to write tests for a view such as the myblog.views.post_detail(request, post_id)
. In order to do that, we're going to utilize Django's test client to simulate the user performing actions towards a view.
The Django Test Client
In the next code snippet, we're going to use django.test.utils.setup_test_environment
and django.test.client.Client
to simulate user's interactions with our website's views.
[python]
>>> from django.test.utils import setup_test_environment
>>> # Set up a test environment so that response.context becomes available
>>> setup_test_environment()
>>> from django.test.client import Client
>>> client = Client()
>>> from django.test.client import Client
>>> client = Client()
>>> # GET the root path of our server
>>> response = client.get('/')
>>> # Inspect the status_code of our server's response
>>> response.status_code
200
>>> # Print the content of our server's response
>>> print(response.content)
No post is made during the past two days.
>>> from myblog import models as m
>>> from django.utils import timezone
>>> # Create a new post so that the next response will return it
>>> m.Post.objects.create(content='New Post', created_at=timezone.now())
>>>
>>> # Print the content of our server's new response, which includes the new post we just created
>>> client.get('/').content
'\n
- \n \n
- \n New Post\n Aug. 14, 2013, 9:12 p.m.\n
\n \n
\n\n'
[/python]
In the previous code snippet, response.context
is a dictionary that includes information about all the keys and values relevant to the current request-response life cycle of our Django server. It won't be available unless we call setup_test_environment()
to make the current shell a test environment. Such a call to setup the test environment is not necessary in tests.py
since it's already constructed by Django during python manage.py test
.
Writing Test Code to Verify Post Detail View
Our existing post detail view returns a post according to the Post.id value passed inside the URL:
[python]
# myblog/urls.py
urlpatterns = patterns('',
...
url(r'^post/(?P\d+)/detail.html$',
'myblog.views.post_detail', name='post_detail'),
...
# myblog/views.py returns responses to requests at paths such as post/1/detail.html
def post_detail(request, post_id):
try:
post = m.Post.objects.get(pk=post_id)
except m.Post.DoesNotExist:
raise Http404
return render(request, 'post/detail.html', {'post': post})
[/python]
Now we can write test cases to verify that the post_detail
view does return a post when the post_id
is provided in the URL and raise a 404 error when the post does not exist. Insert the following code into myblog/tests.py
:
[python]
import sys
from django.core.urlresolvers import reverse
# The function 'reverse' resolves a view name and its arguments into a path
# which can be passed into the method self.client.get(). We use the 'reverse'
# method here in order to avoid writing hard-coded URLs inside tests.
class PostDetailViewTests(TestCase):
def setUp(self):
super(PostDetailViewTests, self).setUp()
self.post = m.Post.objects.create(content='New Post', created_at=timezone.now())
def tearDown(self):
super(PostDetailViewTests, self).tearDown()
self.post.delete()
def test_post_detail_success(self):
response = self.client.get(reverse('post_detail', args=(self.post.id,)))
# Assert that self.post is actually returned by the post_detail view
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'New Post')
def test_post_detail_404(self):
response = self.client.get(reverse('post_detail', args=(sys.maxint,)))
# Assert that the post_detail view is returning 404 for non-existing posts
self.assertEqual(response.status_code, 404)
[/python]
Testing Django Views Summary
In this article, we learned how to write automated tests for views in our Django application. We utilized Django's test client to simulate GET
requests to our server and check the returned responses to make sure that they satisfy different use cases.