If I titled this the way I wanted, it might crash my server

A bit more on rspec and cucum­ber, since last night wran­gling with direc­tory struc­ture in a project we’re doing I finally put my fin­ger right on what was bug­ging me.

As I think I said, cucum­ber is for peo­ple, an almost-​​natural-​​seeming domain-​​specific lan­guage parser that smoothes the con­ver­sa­tion between a non-​​programmer cus­tomer and a pro­gram­mer imple­ment­ing par­tic­u­lar fea­tures. Bet­ter yet (and safer, in my case) it can help frame the notion of util­ity, so that a pro­gram­mer writ­ing code which may be periph­eral or unre­lated to actual fea­tures can dis­cover the actual value to a cus­tomer… or get rid of the chaff.

I tend to be wordy, see. Tend to add fea­tures glibly, or type in extra meth­ods with­out being called to do so. So for me, with my own spe­cial needs as such an unusual crea­ture as a pro­gram­mer who doesn’t pay atten­tion (or as a cus­tomer who doesn’t notice how many fea­tures are being demanded), I think of cucum­ber and rspec as my affor­dances. Keep me from falling down as much as I am wont to do.

Cucum­ber pro­vides an ele­gant inter­face between those two modes of think­ing: between the lin­guis­tic descrip­tion of a fea­ture and the tech­ni­cal def­i­n­i­tion of the cor­re­spond­ing behav­ior. Not just by exer­cis­ing all the cool and ele­gant code in its arse­nal, but also in mod­i­fy­ing the typ­i­cal prac­tice of con­ver­sa­tion between a pro­gram­mer and a cus­tomer: because it demands a degree of lin­guis­tic pre­ci­sion that is lack­ing in many con­ver­sa­tions between these worlds. You “get” to use “plain lan­guage” to describe your fea­tures. But suc­cess requires thought­ful wording.

For exam­ple, this is the cucum­ber code I’m using to man­age a fea­ture in an image-​​processing sys­tem I’m play­ing with. I use it to cap­ture the over-​​arching “mean­ing” of this par­tic­u­lar fea­ture, and also to state clearly when I’ll accept this code as being “done”:

Feature: Process an image in memory
  In order to collect training data
  As a statistical artist
  I want to reduce example files to database entries

  Scenario Outline: base-level block scanning
    Given an image in memory with <height> x <width> pixels
    And a maximum block depth of <maxDepth>
    When I ask for all blocks
    Then I should a list of all the <total> blocks I expect

  Scenarios: low-level blocks
    | height | width | maxDepth | total |
    | 1      | 1     | 1        | 1     |
    | 10     | 4     | 2        | 67    | # 40+27
    | 8      | 9     | 4        | 200   | # 72+56+42+30
    | 1      | 10    | 3        | 10    | # 10 (can't make higher depth than 1x1)

These four sce­nar­ios I’ve described are exer­cis­ing a bunch of edge cases and stan­dard behav­ior, and when they’re imple­mented in func­tional code they will imply all sorts of unmen­tioned things about excep­tion test­ing and error check­ing and all kinds of libraries, and in that code I’ll have to pass all kinds of mes­sages here and there. And note also that they don’t spec­ify what a lot of the words mean, nor spec­ify the names of func­tions (there’s no explicit men­tion of the method Scanner#allBlocks, for instance).

They may look like a flat text file, but in fact they’re very for­mal bases of very par­tic­u­lar chunks of exe­cutable code. They say what I want, and when the cucum­ber story-​​runner turns them all green, I’ll feel this fea­ture has been finished.

And else­where in my project code, in other files for “me, the pro­gram­mer”, there are def­i­n­i­tions that map these for­mal lines into runnable code:

Given /^an image in memory with (\d+) x (\d+) pixels$/ do |height, width|
  ... [code that sets up the infrastructure for the acceptance test]
end
Given /^a maximum block depth of (\d+)$/ do |maxDepth|
  ... [code that sets up the infrastructure for the acceptance test]
end
When /^I ask for all blocks$/ do
  ... [code that creates the test output]
end
Then /^I should a list of all the (\d+) blocks I expect$/ do |total|
  ... [code that compares the test output to the expected result]
end

With all these plain-​​text strings float­ing around, it may seem as if that’s a bunch of wob­bly, ill-​​defined vague­ness in action. But it turns that the use of very spe­cific fram­ing of detailed and focused con­cepts is crit­i­cal for mak­ing the jump from notions to code, or (if by some chance you’re doing things ass-​​backwards) to take already writ­ten code and cap­ture the pure func­tion that it imple­ments. It focuses atten­tion on the bound­aries between “sys­tem under [inte­gra­tion] test” and the rest of the typ­ing you’re doing.

Indeed, now I think about it, a lot of the neg­a­tive com­ments I’ve heard from pro­gram­mers who don’t like the style of rspec or cucum­ber (or test-​​driven devel­op­ment, for that mat­ter) story-​​writing focuses on how wishy-​​washy “plain words” are. But when you push them, it turns out the prob­lem really is about the painful rigor one feels, when forced to map what some­body actu­ally wants to what one is actu­ally typing.

Being mean­ing­ful is hard work.

Now the code I show above, that’s from cucum­ber files, and cucum­ber is for func­tional, inte­gra­tion, accep­tance test­ing. That junc­tion between what some­body actu­ally wants, and what is actu­ally done. Down inside the devel­op­ment process itself, where those lit­tle chunks of func­tion are trans­formed into classes and meth­ods and calls and results, is where rspec comes into play for me, as a unit test­ing frame­work. And what I was say­ing the other day, about the dif­fer­ence (which some con­sider spu­ri­ous) between tra­di­tional assertion-​​based unit test­ing and specification-​​based unit test­ing? That’s a mat­ter of seman­tics. (As evi­denced by, for exam­ple, shoulda, the unit test­ing exten­sion for Rails that lets you frame your stan­dard unit tests in spec-​​like lan­guage).

See, for me the pref­er­ence of specification-​​driven as opposed to assertion-​​driven unit test­ing is not really about being wordy, and want­ing to always use a para­graph when a line of ele­gant one-​​character APL code will do. Though I am. Wordy.

No, it’s really about more com­fort­ably cap­tur­ing the lit­tle intu­itions one gets, when build­ing some com­plex object. Because when I’m pro­gram­ming I con­stantly feel, or I hear, or I know, “This should be over there. This shouldn’t hap­pen this way. This should never come up again.” And whether I frame my actions as unit tests or as spec­i­fi­ca­tions, cre­at­ing new ones should be sim­ple and com­mu­nica­tive, so I know when I’m done, and so I can pose and solve each lit­tle con­cern as quickly and with as much focused atten­tion as possible.

How one states these lit­tle prob­lems is, at its essence, how I (as an ama­teur) see the dif­fer­ence between behavior-​​driven and test-​​driven work: that behavior-​​driven work more clearly sur­faces tele­ol­ogy.

For exam­ple, here’s an extract of rspec code from that same project, where I am imple­ment­ing code that will (even­tu­ally) get those cucum­ber fea­ture descrip­tion steps to work:

module Scanning
  describe TrainingImage do
    describe "when initializing" do
      it "should contain a new image with the given width and height" do
        newImage = TrainingImage.new(10,10)
        newImage.width.should == 10
      end

      it "shouldn't complain when given no parameters" do
        lambda {TrainingImage.new}.should_not raise_error(ArgumentError)
      end

      it "should have a default backgroundColor of white" do
        newImage = TrainingImage.new
        newImage.bgColor.should == '#FFFFFF'
      end
    end
  ... [lots more]

Each one of those “should” phrases is a lit­tle incre­ment of work I con­sciously set up—or that I dis­cov­ered along the way—as I worked on this com­plex object.

As you can see, this rspec code is more… code-​​y. But it still has those fram­ing state­ments of pur­pose star­ing you in the eye, the describe and it "should..." blocks. And when you run the specs, you get a list of pass­ing and fail­ing and pend­ing phrases, not func­tion names or some other kind of code that would be another step removed from explicit description.

And, yes, some­where else in the project hierarchy—frankly it’s pretty much buried—is the actual code that makes it work. It’s a lit­tle, tiny frac­tion of the bytes in a project. But that’s OK. Because (if I do my jobs well enough), every dis­tilled char­ac­ter of that code is sim­pler, eas­ier to under­stand, tested, described, and tied directly to the func­tion­al­ity I actu­ally need to pro­duce busi­ness value.

Now I men­tioned above, last night I finally put my fin­ger on what it was that was both­er­ing—pain­ing—me the other day about both cucum­ber and rspec, about my expe­ri­ences in using them, and about intro­duc­ing these tech­niques to other peo­ple. And it’s these intru­sions:

require File.join(File.dirname(__FILE__), "/../spec_helper")

and

$: << File.join(File.dirname(__FILE__), "/../lib")

Ouch. No. Please make it stop.

These lit­tle nuggets of dense, inscrutable Ruby are the links between the many files in a stan­dard cucum­ber- or rspec–dri­ven project direc­tory. They crop up at the top of files now and then (and often with no appar­ent cause in the man­u­als and tuto­ri­als past “this has to be here for it to work”). Some files need them, some files don’t. To under­stand which files need these lines at the top, and where the links need to point, and what the links have to spec­ify and the syn­tax to use… you need to under­stand what the cucum­ber and rspec libraries are actu­ally doing.

You need to fig­ure out what it is the algo­rithms want.

Now, it you’re famil­iar with Rails pro­gram­ming, you’ll rec­og­nize the pat­tern because it also appears in most Rails projects, what with the deep many-​​branched mag­i­cal direc­tory struc­ture in that frame­work. And there, as here, they are a frickin’ pain in the butt.

Why am I rail­ing against such a lit­tle incon­ve­nience of some nasty perl­ish Ruby code? Well, think about it: In the midst of these two rather ele­gant domain-​​specific sys­tems (and for that mat­ter in the midst of mag­ickal mys­ti­cal Rails, of all places), where it almost seems like nat­ural lan­guage pars­ing is hap­pen­ing right there before your eyes, and which exist only to sur­face the link between desire and imple­men­ta­tion as com­fort­ably as pos­si­ble, you have to say some shit like “$: <<”? This is not peanut but­ter on my choco­late bar, this cuts the eye.

OK, so work with me, here. One has already cre­ated a suite of pleas­ant, linguistically-​​smooth func­tion names like Given and When and should_not and should have. And the mys­ti­cal libraries that inter­pret them are stuffed down inside some gems so real peo­ple need never bother them­selves with the ugly stuff they actu­ally do. Yay!

But before you can use those ele­gant libraries, you need to say these magic phrases? This is what, some kind of reminder of our mor­tal­ity, some kind of inten­tional flaw in the sym­me­try of a beau­ti­ful Per­sian rug?

Bah. As an ama­teur pro­gram­mer myself, who has gone out on a limb so far as to insist my beloved wife pair pro­gram with me as I learn to use these frame­works bet­ter and remind myself of Ruby code after many months in Python land… let me tell you that the last frickin’ thing I want is for the first line of the code that makes the won­derul thing I’m tout­ing actu­ally run to be some­thing I have to look up every char­ac­ter in the man­ual to under­stand, and I still can’t fig­ure out which files have to have it and why.

Now, maybe I don’t under­stand enough about the struc­ture of these libraries. I’m not sure it’s pos­si­ble to use the same kind of pars­ing lan­guage they already use—you know, the ones they already have baked-​​in to take clauses of text and trans­form them into object instances? Whether maybe those could be brought to bear to cre­ate func­tions like Collect all_specifications.from "/../lib", or maybe FirstLoad "spec_helper.rb"?

I’m not really a very good pro­gram­mer, as it hap­pens, and I sus­pect doing any­thing to write these myself is way past my abil­i­ties. But I have this weird feel­ing it’s fea­si­ble to add these lit­tle affor­dances for stu­pid peo­ple like me. And that maybe, since they crop up all over in big multi-​​file projects, they might actu­ally be useful.

One thought on “If I titled this the way I wanted, it might crash my server

  1. I wrote quite a bit of perl code in my day (back in the day when I wrote code longer than one line) that went

    if (/interesting.pattern/) {
    some­thing();
    something(else);
    }

    until I learned perl and they turned into

    /interesting.pattern/ && (some­thing(), something(else));

    The one line you com­plain about in ruby is cer­tainly unpack­able and pro­noun­ca­ble and can be turned into about a page of C, check­ing error codes and recur­sively tra­vers­ing direc­to­ries all the way. I don’t think in ruby so I can’t help you on the details, but I hear your pain when line noise intrudes on thinking.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>