Andrew Bennetts writes why narrative tests are lousy unit tests. I completely agree.
Narratives are great as documentation, and the embedded doctest sections help (1) keep the documentation up-to-date and (2) provide concrete examples that make the documentation easier to understand. Here's a good example of that: Storm ORM tutorial. But for unit tests you want many small, isolated tests rather than one big narrative, for reasons that Andrew so clearly elucidated in his post.
My preferred way of writing unit tests is a mixture of unittest and doctest:
import unittest import doctest from cStringIO import StringIO from mysuperduperpackage import Gronkulator def doctest_Gronkulator_parseXML(): """Tests that Gronculator.parseXML does the right thing >>> g = Gronkulator() You pass a file-like object to Gronkulator's parseXML(): >>> g.parseXML(StringIO(''' ... <gronk id="g42"> ... <item id="a">One</a> ... <item id="b">Two</a> ... <item id="c" important="yes">Three</a> ... </gronk> ... ''') and the items are loaded into ``g.items``: >>> for item in g.items: ... print item.id + ':', item.title, '!' if item.important else '' a: One b: Two c: Three ! """ def doctest_Gronkulator_parseXML_error_handling() """...""" def test_suite(): return doctest.DocTestSuite(optionflags=doctest.NORMALIZE_WHITESPACE) if __name__ == '__main__': unittest.main(defaultTest='test_suite')
I've written more about this on Zope mailing lists on several occasions.