TDD with Pylons
I’ve written about test driven development and Pylons before but there have apparently been some changes to how it all works since Pylons 1.0. I didn’t run across anything in the documentation detailing the changes, specifically to the template context global and how you access it in your tests.
From the Pylons docs on testing:
Pylons will provide several additional attributes for the paste.fixture response object that let you access various objects that were created during the web request:
- session — Session object
- req — Request object
- c — Object containing variables passed to templates
- g — Globals object
To use them, merely access the attributes of the response after you’ve used a get/post command:response = app.get('/some/url') assert response.session['var'] == 4 assert 'REQUEST_METHOD' in response.req.environ
As it turns out, that’s sort of true. I have a test that looks like this:
from nbapowerrank.tests import * class TestGamedetailsController(TestController): def test_index(self): response = self.app.get(url(controller='admin/gamedetails', action='index')) self.assertEqual(len(response.c.games) > 0, True)
and a controller that looks like this:
from nbapowerrank.lib.base import BaseController, render from nbapowerrank.model import meta from nbapowerrank.model.game import Game log = logging.getLogger(__name__) class GamedetailsController(BaseController): def __before__(self): self.game_q = meta.Session.query(Game) def index(self): yesterday = datetime.today() - timedelta(1) c.games = self.game_q.filter_by(gamedate=yesterday).all() return render('/gamedetails.mako')
Unfortunately, contra the documentation that says the “c” alias will be available on the test response object, that test always fails with an AttributeError stating that in fact, the c attribute does not exist. It frustrated me even more because all the other attributes that are supposed to be on the response like session and g were in fact there. After doing some random digging, I came across this in the Pylons 1.0 roadmap: “Deprecate pylons.c, pylons.g, and pylons.buffet. These have been disrecommended since 0.9.7.”
Apparently, they are/have deprecated using the c alias for tmpl_context even though when you create a controller under Pylons 1.0, it still aliases the context as c. Sigh. So in order to test data that you have added to your context for templating, your test should use the explicit tmpl_context instead of the c like this:
from nbapowerrank.tests import * class TestGamedetailsController(TestController): def test_index(self): response = self.app.get(url(controller='admin/gamedetails', action='index')) self.assertEqual(len(response.tmpl_context.games) > 0, True)
And then all will be well with your world. I do have to say that compared to both ASP.Net MVC and Rails, the testing support in Python/pylons seems to be a second class citizen. I’m not sure why that is because as a dynamic language, it seems to benefit greatly from a TDD approach. Maybe it’s just me getting back into a framework after a year’s worth of changes.
Anyway, that may help some people out there trying to search in the googleplex about TDD and Pylons.