My latest Safari book is Python in a Nutshell a very comprehensive reference guide to python, plenty to learn. In some places, like the standard library section, it just summarises what the module does in a way that is not so enlightning but I have picked up some stuff in passing that I was not aware of before. The standard python documentation is very dry and it can be hard to find a useful descriptions. Python in a Nutshell does explain things in a more understandable way and it does include more example code. I may well keep the safari book in my bookshelf permanently (or buy it) to use in parallel with the python documentation.
I read the section on testing which described the doctest and unittest modules. I have tried docstring in the past, it involves putting test scripts in the docstrings of each function and to my mind this made the code bloated and hard to read.
I hadn't tried unittest before so I gave it a try with some django code, specificly code to convert a string of tag names into correct database entries. I started off doing it the Right Way by writing the tests first, before I started coding. It was a good exercise, just thinking of what I wanted and what could go wrong before I started to code.
The unittest module requires creating TestCase classes with three phases of testing:
- a setUp function to initialise things
- various tests in arbitrary order (because each test is a method and the methods are stored in a dictionary there is no guarantee on what order they will be executed)
- a tearDown function to close things up
In my case of manipulating the database I needed setup and teardown code for each individual test to ensure that the correct tags and tag->post mappings were created for each tag string under test. I could have created multiple TestCase classes, with seperate setUp and tearDown functions for each individual test but that seemed like too much typing. The problem here was really due to the arbitrary order in which the tests could be performed: each test cannot rely on what state the preceeding test left the database in so each test has to set the database up first.
As I slept last night I tossed and turned with this on my mind. Writing lots of setUp and tearDown code just to get around the arbitrary order. I could write just one big test function that went through a sequence of tests but it felt like cheating. To make this worse, I looked at the django tests and they used something similar to docstrings but in seperate files, a straight sequence of tests as if typed into the command prompt. It looked good and reading it is like reading example code. However, I have two problems with working this way:
- it needs more editing to create the script than unittest
- because all the code is inside a string, it is all one colour, I lose syntax highlighting. Do I need editor macro's to toggle the string delimiters on and off?
I think what I will do is use unittest without excessive setup and teardown code and just write long test functions. The aim of the exercise after all is to test, not to write test code.
Conclusion: testing: keep it simple and don't lose sleep over it.
Update: single stepping through the unittest code with WingIDE, it seems that the test case methods are sorted by name using the cmp function so they are probably alphabetic. Hence I could just name my functions test_001, test_002 etc or I write something to determine line numbers, or I define an array of test functions...
Twitterings

If it hurts to move that way - stop moving that way. Unit tests *must* run quickly. If your testing is slow because of your database, then get rid of your database. You shouldn't be hitting disk i/o during a unit test anyway. Abstract out your code a little and sneak in a SQLite ":memory:" backed database and your tests will go much faster.
As for unittest instantiating a new testcase instance per method -that's actually a design feature carried over from JUnit/SUnit. Tests should be independant of each other, and the easiest way to ensure that is to make each testcase method run in it's own testcase instance.