Tuesday, July 16, 2013

Django doctesting with WebTest

I’ve been a big fan of Python’s doctest since I first worked with Zope 3. I know a lot of people knock it, but there was always a sort of "magic" in pasting use-cases into rst documents, inserting some python and and you're done.

Recently I've been working on a number of Django applications and I really wanted to re-use this pattern.

Initially, I used the built in django.test.client - this was a fairly close approximation of Zope's testbrowser and lead to doctests like:

Where this falls down is the testing of forms - most recently I was testing the uploading of a file and the various server-side validations that would trip (name, size, contents etc). To do this in django.test.client, you must use the post() method with the following result:

The testing of file uploads is even worse.

Trying to solve this problem I came across this excellent slideshow about using WebTest. This looked like the perfect solution with its simple form access and file upload wrappers. Combining WebTest with django webtest gave me a very similar base API to django.test.client.

Here I ran into a problem though. All the demos and documentation for WebTest showed usage in unit tests. A Google search for "doctest WebTest" wasn't helpful either. Pulling out Python's dir() function, I discovered a very interestingly named class DjangoTestApp in django_webtest. A couple of minutes later and my doctests looked like this (abbreviated):

The best bit was the actual uploading of files - the "name" and "content" is just assigned to the field on the form:

This is an incredibly elegant interface and allowed me to quickly perform a huge range of upload testing.

Why not just use unittest, you ask?

Simply put, a doctest can be handed to the client, my line manager or any co-worker, and they can line it up against a set of functional requirement, or just their domain knowledge. The same simply cannot be said for something like the following (from here):