Personal tools
Document Actions

Django Unit Tests and Transactions

by Dan Fairs posted on 2008-07-01 21:48 last modified 2008-07-07 11:51 —
While these are more properly integration tests than unit tests, it can be handy to have Django roll back the database transaction after each test method runs.

Coming to automated testing in Django from the Zope and Plone world, I was pleased to find full support for all the testing machinery that I've become used to: regular Python unit tests, and doctests. Of course, these being unit tests, they don't do any 'framework' management out of the box.

Unit tests are supposed to test your code, and just your code. However, once you're in a framework environment (be that Zope and Plone, Django, or anything else) then testing how your code integrates with that framework is vital. Zope and Plone provide unittest.TestCase subclasses (ZopeTestCase and PloneTestCase respectively) which provide a lot of scaffolding for you to be able to run integration tests. Part of that scaffolding is automatic transaction management. This hooks into Zope's transaction API to roll back the transaction after each test runs.

I wanted to do something similar for my Django test cases; I was finding 'state pollution' between my unit test runs, since data created by one test method isn't automatically cleaned out.

Django's transaction handling is much simpler than Zope's: it cares only about the one database transaction that the current request has, and only if the transaction support middleware is installed. This means that we can pretty easily crib the code from that middleware and use it in a test case base class:

from django.db import transaction

class TransactionalTestCase(unittest.TestCase):

def setUp(self):
super(TransactionalTestCase, self).setUp()

transaction.enter_transaction_management()
transaction.managed(True)

def tearDown(self):
super(TransactionalTestCase, self).tearDown()

if transaction.is_dirty():
transaction.rollback()
transaction.leave_transaction_management()

UPDATE: Fixed an error in the call to the base class' tearDown() method, which caused open transactions to hang around and (among other things) prevented the test database being cleanly dropped at the end of the test run.

After this, you can simply derive your test fixture classes from TransactionalTestCase, and make sure that you call the base setUp() and tearDown() methods if you do need to override them to perform your own setup and teardown.

My next spare time (hah!) project will be to integrate Django's transaction management into repoze.tm (which is Zope's transaction management suitably WSGI-fied). This would let a Django application participate in transactions with other transaction-aware components, making integration at the WSGI layer much more straightforward.

A small patch

Posted by Osvaldo Santana Neto at 2008-07-01 23:59
def tearDown(self):
- super(TransactionalTestCase, self).setUp()
+ super(TransactionalTestCase, self).tearDown()

Patch

Posted by Dan Fairs at 2008-07-28 21:21
Good spot - fixed.

Django TestCase

Posted by Remco Wendt at 2008-07-04 07:48
Hi Dan,

I think you're better off using Django's testcase (django.test.TestCase) which does exactly this and more.

Remco

Django TestCase

Posted by Dan Fairs at 2008-07-28 21:21
Yep - spot on! I seem to have a habit of only finding out about things *after* I've written something similar. Must get to know Google better...

Thanks for the comment.
 

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: