TDD With Python and Pylons

I’ve been doing some devel­op­ment on a web­site using Python and the Pylons web frame­work. I’m try­ing to stay pretty strict with Test Dri­ven Devel­op­ment (TDD) though I run into prob­lems because I’m still a com­plete novice with Pylons and a half-complete novice with Python. In my expe­ri­ence so far, unit tests are mod­er­ately dif­fi­cult in Pylons and turn out to be some­thing closer to the bas­tard stepchild of a red­neck unit test and a 5th Avenue socialite mom. I feel that way because the unit tests require the Pylons frame­work to be set up cor­rectly. They also often go out­side their bound­aries and I haven’t looked into any mock frame­works, though I have the feel­ing that using a mock frame­work with a dynamic lan­guage like Python is prob­a­bly a stu­pid thing to say in public.

Regard­less, I have really started to enjoy work­ing with Pylons and that stems from the actual func­tional tests that are avail­able through the frame­work. Specif­i­cally, my devel­op­ment flow has been some­thing like this:

  • Write new func­tional test of the web site
  • Run tests to see them error out. Typ­i­cal error mes­sage is that an action isn’t imple­mented on the controller.
  • Imple­ment the basic con­troller and the action but leave out the func­tion­al­ity under test.
  • Rerun the test to see it fail.
  • Imple­ment the func­tion­al­ity nec­es­sary to get a pass­ing test. This often includes imple­ment­ing data­base tables, keys, get­ting SqlAlchemy set up to cor­rectly map data to objects and cre­at­ing new tem­plates for HTML.

The func­tional tests are nice because while they aren’t con­firm­ing look and feel type stuff, they at least put the flow of the appli­ca­tion under test. I’m a purist when it comes to hav­ing unit tests only test the code they are intended for but I’m not a purist when it comes to writ­ing unit tests first as the only way to design the appli­ca­tion. With a web­site like this, I’m pretty happy writ­ing func­tional tests to drive out the design of the web site.

Here are some code snip­pets from my cur­rent web­site (try to ignore the fact that this looks like it might have some­thing to do with horses and the Ken­tucky Derby, espe­cially if you work for the NSA.)

First a test:

from darlydowns.tests import *
from darlydowns.model import meta
from darlydowns.model import horse


class TestHorseController(TestController):
    def test_index(self):
        # setting up a temp horse to make sure one exists for the test
        tempHorse = horse.Horse('my temp', 'my description')
        meta.Session.save(tempHorse)
        meta.Session.commit()
        
        response = self.app.get(url_for(controller='horse'))
        ## Test response...
        assert len(response.c.horses) > 0

        meta.Session.delete(tempHorse)
        meta.Session.commit()

Here we have a test that tests the response from a request for a URL that is han­dled by the con­troller “horse”. This con­troller grabs all the horses in the data­base and dis­plays them in a table. The test saves a temp horse, gets the list, ver­i­fies the list con­tains at least 1 horse and then deletes the temp horse to clean up after itself.

Here’s the con­troller code that allows the test to pass:


import logging

from darlydowns.model import meta
from darlydowns.lib.base import *
from darlydowns.model import horse
from darlydowns.model.horse import Horse

log = logging.getLogger(__name__)

class HorseController(BaseController):

    def index(self):
        c.horses = [horse for horse in meta.Session.query(Horse).all()]
        return render('/horses.mako')

One of the beau­ties of Pylons is how lit­tle code is required to do some­thing, once the project is set up. Here our HorseC­on­troller has an action of “index” which is defined as a method. It grabs all the horses from the data­base and then uses a list com­pre­hen­sion to col­lect them into the c.horses vari­able. It then ren­ders the tem­plate “horses.mako” which knows how to lay­out the web page using the horses found in the database.

Once this was done, I wrote code to save a horse, all dri­ven out by the func­tional tests. I’ve been pretty happy with how the design is dri­ven from these tests as it’s often quite clear where to pro­ceed next in the appli­ca­tion from the last test. Lots of times, other func­tion­al­ity comes up that doesn’t log­i­cally flow next but I just add that to a grow­ing todo list in the project to make sure noth­ing is skipped.

Pylons takes a lit­tle get­ting used to, espe­cially since I come from a sta­tic, every­thing in once solu­tion sort of back­ground. With Pylons, you need to learn Routes and Mako and SqlAlchemy but once you get your head around all those tools, it’s a joy to work with.

UPDATE: Wel­come to every­one com­ing here from the Python sub­red­dit. If you have any tips for test­ing using Pylons, feel free to drop me a com­ment. I’d love to hear about other people’s experiences.

2 Comments

Leave a Reply

Your email is never shared.Required fields are marked *