a test suite for ice cream (1/?)
So, most days, I think I am pretty good at my job, which is [nominally] to write automated functional tests for web applications using twill and nose (and sometimes selenium RC, when I have exhausted all other possible options). That’s what I do, and I don’t suck at it, but often I feel kind of trapped in this silo of narrow knowledge where I can write kickass test generators but then I go and ruin my carefully crafted illusion of competency by saying things like, “tuples can only have two things in them, right? That is why they are called tuples? TWO-pules? Right? … guys?”
Anyway, yesterday I was having problems with variable scope, and today I managed to write a test suite for a new app we’re building, and I am pretty pleased with myself. (It still has some problems, but more on those later.) I don’t know how many people use twill + nose to do the things we do — I never find much about it on the interwebs — so here’s the skinny.
We have a lot of applications that are hooked up to many domains, so while we have many sites (hundreds of them, in fact), we really only have a handful of applications. So the problem has always been something like this:
Let’s say you have come up with a way to sell ice cream over the internet. You set up your site, www.icecreamrules.com, and everything is peachy. Business booms! You decide that you should also sell gelato, so you make www.gelatorulesmore.com, and you build it as basically a skin of the ice cream site. One app, two sites, same functionality.
How do you test it? You could write every test twice, but that’s dumb. It quickly becomes unmaintainable when you decide to also sell frozen custard and milkshakes and sno-cones and Hawaiian shaved ice and those strangely tasty low-fat fudgesicles.
From there, it gets even more complicated because the different types of frozen treats require different site behavior. Most sites have three pages (pick your flavor, where should we send it, thanks for playing), but the milkshake site has an extra page that wants to know if you’d like your milkshake really thick, or thin and drippy. The low-fat fudgesicle site skips the flavor question entirely (it’s fudge-flavored, yo) and just sends you straight to the ordering page.
And THEN maybe you decide to change it up a little and add chocolateicecream.com, which does the same thing except instead of asking for flavor, it asks you for type (gelato, custard, pick your poison). Your app is getting more and more complex, and the tests have to keep up.
So is it still crazy to write a separate test suite for each site? … yeah, still pretty nuts. Okay!
So the best idea we’ve been able to come up with is that there has to be some way to pass parameters to nose via the command line: nosetests –with-site=icecreamrules.com, or nosetests –with-flavor=chocolate. Each site has a configuration file with the relevant details, and the tests rip through them, generate the dataset, go the sites, and make sure everything is copacetic. Right?
Assuming that yes, we’re right, step one is the nose plugin. The code itself is pretty simple, actually, but it requires a lot of thinking about how you’d like to build your suite and what sort of control you’d like to have over the tests. Maybe for our ice cream app we want to run according to site, flavor and type? So the plugin will look something like this:
from nose.plugins import Plugin
class IceCreamParams(object):
ice_cream_flavor = None
class IceCreamPlugin(Plugin):
name = 'IceCreamPlugin'
def options(self, parser, env):
parser.add_option("--with-flavor", action="store", dest="flavor",
default=None, help="Run against a given flavor.")
Plugin.options(self, parser, env)
def configure(self, options, conf):
Plugin.configure(self, options, conf)
IceCreamParams.ice_cream_flavor = options.flavor
Obviously that is only for flavor, but it should be pretty clear how to add more options. Once that’s done, write a setup.py file and you’re good to go. IceCreamPlugin will eat up the value you pass in the command line, and that will all live in your IceCreamParams container object. From there, you can write modules to consume that object, by importing it and then calling whatever values you’ve set.
I was going to write up the whole thing, but then I realized that documenting the process is going to take me way longer than it did to actually write the code and I wanted to post something while it was fresh. I will continue showing off my ice cream app tests at a later date, probably by discussing the site configs. Eventually we can hook everything together, and it will be 16 kinds of cool.
But before I do that, I should point out that the shiny test suite I wrote this week was built largely on the bones of the one built by Terry Peppers (with help from Kumar McMillan and Jason Pellerin and Luke Opperman and Chris McAvoy and probably a bunch of other people), and I have basically just cannibalized it for my own whimsical purposes. Terry may also be writing about this whole testing with twill + nose thing, but he has less free time than I do, so I figured I might as well start.
Thoughts?
Tags: nose, plugins, python, testing