Wednesday, 27 August 2008

Django testing for absolute beginners

I want to interrupt my series of posts on Practical Django Projects and quickly show how to setup the most basic of tests, and hopefully convince you why taking some small steps and starting with one line of test code can save your sanity especially if you're tracking a code breaking trunk. First the why..

In my previous post I wrote about getting an error in the browser:

ImproperlyConfigured at /admin/
Error while importing URLconf 'cms.urls': 'type' object is not iterable

The problem with this is that the error could be in any one of a number of modules that are imported by urls.py. There are a number of ways code could cause Django to generate this kind of error if you have problems in your code - it just happens that in this case it's newforms admin. Here's the ticket if you're interested.

I spent hours trying to work out this problem because aside from the actual cause of the problem I dug my own grave with a very stupid python mistake. After working out that it was admin files that were causing the problem I renamed one to try and establish which one was causing the problem, but this left the corresponding admin.pyc which python happily kept importing and runserver kept regurgitating the same useless error message. 

Remember, if you remove or rename a python file make sure you remove the corresponding .pyc file.

Ok so you haven't made my stupid mistake, but you're still irritated at having to comment out the admin.site.register function in each admin.py to find your mistake. Well you can save yourself the grief if you create the simplest of tests which in fact would have solved the problem with the redundant .pyc file as well.

Creating a simple tests.py for your project or app will save your sanity and takes almost no time.

Basically every python programmer should know the basic principles of testing using doctest and unittest. Python has the advantage of being able to hack something really quickly, but the temptation is to turn your hack into fully fledged apps without writing tests because it seems quicker. But try coming back to your application a year later and enhancing it. I guarantee you if you have a basic standalone doctest file you'll be up to speed again in no time instead of trying to work out why you'd written some function or trying to debug it. Doctests also seem to help highlight when your app is poorly designed to start with. If it looks wrong in a doctest chances are you want to go back and rework your code. If your app grows in scale then doctests can become unwieldy so you're probably going to want to put in unittests to test more than just basic usage. Ok enough about the principles of why you should write tests, lets look at those basic tests I promised .

First create a tests.py in any project or app directory that holds your apps that get imported by your INSTALLED_ APPS in settings.py. So along side your admin.py, models.py etc. Also if there is no models.py create that as well because the django test runner needs that to know it should load your tests.py.

In your tests.py import whatever modules you have in that directory that have been modified by you. So in the cms example from Practical Django Projects my tests.py would be:
#tests.py
from cms import admin, models, settings, urls, views


That's it! Then go and run python manage.py test  to reap the rewards from your labour. Hopefully it should run a bunch of tests, print OK and end - in which case you're free to go. But lets look at how this one line would have got me out of trouble with the Improperly Configured error if I'd written and run the tests first.

Firstly if I have any syntax error or other basic error in any of my modules, I'll see instantly which one from the test traceback, and all I need to do is fix the error in the module and re-run the tests until I get an OK. Try it. Break your own files and see what happens. Secondly, I would have imported admin in my test so when I renamed the broken admin.py file the test runner would still bring up the error in admin.py alerting me to the fact that it was still being imported. In this case I now want the error to tell me that tests.py can't import the admin module. Once it does this I then know I just need to update my tests.py removing the admin import.

Ok that's all well and good if I was going to write a unittest but how about doctests. Well here's the most basic doctest that does the same thing.

#tests.py
tests = r"""
>>> from cms import admin, models, settings, views, urls

"""

__test__ = {"cms_tests":tests}
Ok so thats enough from me. Go have a look at the official django docs on testing and this post and start writing some proper tests.

1 comments:

deveritt said...

You say create a tests.py in any project or app directory that holds your apps that get imported by your INSTALLED_ APPS in settings.py, but for clarity, wouldn't it be best (for Practical Django Projects) to just say, create a tests.py file in your cms directory, alongside settings.py? After importing everything from coltrane into the tests.py file, that worked fine for me, without a models.py file?

Thanks for the post, though! It got me using tests for the first time since starting with Django :-)

Hedged Down