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).