Cover your nose

Python has some great features for testing. One of my favorites has always been doctest, which allows you to embed executable tests right in the documentation of your modules, classes, and functions. There are some practical limits to what you can do with doctests, and that's where unit testing comes in.

Until recently, I've used the builtin unittest module for unit testing, but I've always felt a little bit uncomfortable with it, though I couldn't have explained why until I read Ian Bicking's rant against unittest. I think one of the reasons I'm not crazy about unittest is that it doesn't feel very pythonic. Maybe this is because it was based on a Java framework, which in turn was based on a Smalltalk framework. Why should I have to write boilerplate class wrappers for all my tests, when all I want to do is call some functions? In fact, this characteristic is one of my least favorite things about Java--that "object oriented" really means "objects mandatory." Ironically, in Java, many primitive data types aren't actually objects, unlike in Python where everything really is an object, even though Python isn't considered "object oriented". But I digress...

There's a testing tool (I hesitate to call it a "framework") that I'd heard about called nose. I decided to give it a try, and I'm very glad I did--it makes unit testing about as painless as it can possibly be, with no boilerplate code, no configuration, and only a few simple rules about how your modules and functions should be named. Much in line with duck typing, if it looks like a test, it's a test.

Nose also comes with support for coverage, making it really easy to see at a glance what percentage of my code is covered by tests.

When I started using nose and coverage, there were only a handful of doctests and unit tests in CSVSee. Less than 24 hours later, I've reached 100% statement coverage of the core modules, and made many small improvements to the design as a result.

It's a long way from SQLite's legendary 650:1 tests-to-code ratio, but it's a darn good start.