Unit Tests in Django

I’m going to talk a bit about some of the practices that I use for unit testing in Django.  If you’re unfamiliar with the Django testing system, you can read up on it here: http://docs.djangoproject.com/en/dev/topics/testing/

In general, I organize my unit tests into folders (ref. Organizing Django Tests into Folders) so I can break them down into manageable chunks.

Each test case imports TestCase and Client as such:

from django.test import Client, TestCase

Once that’s out of the way, we can go ahead and define test fixtures and test cases:

class MyTestFixture(TestCase):

    #setUp is always called by Django tests before any of the test cases run
    def setUp(self):
          self.client = Client()

    def test_my_tc1(self):
          #do stuff

    def test_my_tc2(self):
          #do other stuff

Test Fixtures vs Test Cases

Conceptually, a class within your test file represents a particular test fixture. A test fixture represents a conceptual state of the application that you are testing. Multiple test cases can live inside a test fixture, and multiple test fixtures can live inside a test file.

You can use the special setUp method to run code that will change the state of the program before your tests execute. For example, you can create a user in your webapp and login as that user:

 def setUp(self):
        self.client = Client()

        registration_form =  {"username": "foobar", "password1": "mypass1", "password2": "mypass1"}
        self.client.post('/register/', registration_form)
        self.client.login(username='foobar', password='mypass1')

Once the setup is done, you can add test cases to the fixture by creating new methods.

Think before you add a test case! If your previous test case does not modify the state of the app, add the test case. If your previous test case changes the state of the app (new database entry, new user, etc.), create a new fixture.

You can also use test fixtures as a way to conceptually group test cases that test similar functionality.

Common Test Routines

Lets go through some common tests:

Access Webpages

#access the homepage and check for a HTTP 200 response code
response = self.client.get('/')
self.assertEqual(response.status_code, 200)

Check Redirects

#access the homepage and check for a HTTP302 redirect
 response = self.client.get('/')

self.assertEqual(response.status_code, 302)

#here, '/nextpage/' is the expected URL of the page we are redirected to
self.assertRedirects(response, '/nextpage/', status_code=302, target_status_code=200)

Verify Templates

#you can verify that certain templates were used when opening a page
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'mytemplate.html')

Error Checking on Views

#you can verify that error forms are being sent back
form = {'old_password': '', 'new_password1': '', 'new_password2': ''}
response = self.client.post('/password_change/', form)

self.assertFormError(response, 'form', 'old_password', [u'This field is required.'])

Testing Models

First, you’ll need to import your model:

from [project name].[app name].models import [table name]

Now you can access the model as you would in the views; doing queries and selections on the model. This is useful to make sure that entries were update properly in the database (or better yet, to make sure that the database is unaffected by form errors).

This entry was posted in Django, Tech and tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>