Ian Bicking: the old part of his blog

Building testability into web applications

Well, besides setup scripts the other thing I want to take from Rails is building testability into the system. But I haven't quite figured out how to do that. The specific idea I want to take is that of data fixtures -- data that puts the site into a "known" state that can be tested against. In the Rails world that's a bunch of rows to be inserted into the database, which is a particular case I'm interested in, but of course it should be more general.

That's not too terribly hard; it's mostly grunt work. Not super-simple, but not hard. I've added the necessary configuration file hooks to handle different kind of environments running out of the same directory: include() and load(), both of which have access to previous files, so you could do:

if debug:
    include('database_debug.conf')
else:
    include('database.conf')

Or any number of things, like testing if profile == 'debug'. This kind of flexibility (among other things) makes Python files very useful; I was skeptical at first about Python syntax (having invested much time into an INI-file based configuration), but it makes so many things so easy.

Oh, but back to the actual testing. One thing I like about how things have shaped up in Paste is that testing has been pretty easy. paste.wsgilib has a function raw_interactive(app, path_info) that is quite useful, and I use it in paste.tests.fixture in fake_request(). It lets me do things like:

res = fake_request(p, '/')
res.all_okay() # Asserts status is 200 OK, no errors logged
assert 'Welcome' in res
assert res.header('content-type') == 'text/html'

And so on. There's some things I feel should be tweaked. For instance, it should normal throw an error for any status besides 200 OK (with a keyword to explicitly say that another status is okay or expected). Maybe test for compliant HTML (paste.wdg_validate is a start for this). It should make requests a little easier to perform -- maybe it should be a wrapper around an application, for instance. But it's pretty close as it is, it just needs a little loving to get all the convenience methods in (and a good testing framework is all about convenience). And then you could do the whole thing in doctest, maybe like:

>>> app = FakeApp(this_app)
>>> res = app.post('/login', data={'username': 'bob', 'password': 'bob'})
>>> res.element(id='message')
<div id="message">User 'bob' logged in</div>

And so on. You could put this right into your object's docstrings. Using things like doctest_xml_compare you can make your test say just what you mean, and no more.

That could be really sweet. Interested? Join paste-users and we'll talk there.

Update: There's been some progress on this in paste.tests.fixture.TestApp

Created 10 May '05
Modified 11 May '05

Comments:

Do you have an RSS feed for this site?

# Nigel Thorne

Auto-discoverable, yes; it's at http://blog.ianbicking.org/feeds/new_pages.xml

# Ian Bicking

Awesome. Exactly what I was looking for. Loved pbp and twill, but couldn't integrate it very well with py.test. Once you go py.test, you can't go back. :)

# Kevin Smith