Tuesday, May 26, 2009

Bold Cucumber Scenario

‹prev | My Chain | next›

Another Cucumber scenario went green yesterday. Green, but not much bold:

Bold text in Cucumber output indicates that it is consumed for direct use when the running individual steps. For example, consider the first Given step:
Given a "Even Fried, They Won't Eat It" meal enjoyed in May of 2009
This steps builds a meal, using the text "Even Fried, They Won't Eat It" as the title and the text "May of 2009" as the date for the meal.

As much as possible, it is a good idea to use Cucumber text when implementing the steps behind them. Re-using text means that the human readable text in the scenario is usually matching up with human readable text in the application itself. As a general rule of thumb, the more human readable a web page, the better.

There is no bold text in the four should / should not steps that check for links to dates. Each of those has to hard-code the application representation of those date:
Then /^I should see a link to May of 2009$/ do
response.should have_selector("a", :content => "2009-05")
"May of 2009" is easier to read, which is why I wrote it that way in the feature description. I should have carried that readability over into the application. If I do so now, I can also increase the bold in the Cucumber output, DRYing things up a bit as well.

First up is a helper to translate date fragments like "2009-05" into "May 2009". In other words:
describe "month_text" do
it "should increase readability of an ISO8601 date fragment" do
should == "May 2009"
This can be implemented with a little help from strftime:
    def month_text(date_frag)
Date.parse("#{date_frag}-01").strftime("%B %Y")
To get the navigation between month to use this date format, I would like to pass it to the link_to_adjacent_view_date helper as an optional block:
    it "should link to block text, if supplied" do
link_to_adjacent_view_date("2009-04", @count_by_month) do
should have_selector("a",
:href => "/meals/2009/05",
:content => "foo")
More specifically, I want the link_to_adjacent_view_date helper to yield with the adjacent date fragment so that it can be humanized by month_text:
    it "should link to block text + date, if block is given" do
link_to_adjacent_view_date("2009-04", @count_by_month) do |date|
"foo #{date} bar"
should have_selector("a",
:href => "/meals/2009/05",
:content => "foo 2009-05 bar")
Getting those two examples to pass is a simple matter of yielding appropriately in the helper. Something along the lines of:
        link_text = block_given? ? yield(next_result['key']) : next_result['key']
With those examples passing, I update the meals-by-months Haml view such that the navigation links use the new optional block and the humanizing month_text helper:
=link_to_adjacent_view_date(@month, @count_by_year, :previous => true) { |d| month_text(d) }
=link_to_adjacent_view_date(@month, @count_by_year) { |d| month_text(d) }
Finally (after verifying that all specs still pass), I work my way back out to the Cucumber scenario. I remove the "of"s from the steps so that they read "I should see a link to May 2009" (instead of "May of 2009"). Then I can consolidate the step implementations down to:
Then /^I should see a link to (.+)$/ do |date|
response.should have_selector("a", :content => date)

Then /^I should not see a link to (.+)$/ do |date|
response.should_not have_selector("a", :content => date)
Running the scenario now, I find much more bold text in the output:

A little bit of refactoring and my code is DRYer and more human-readable.

That completes this red-green-refactor cycle. Tomorrow, it's on to the next scenario.


  1. Gosh, i never knew that Cucumber worked like that. I simply assumed that it marked in bold the arguments from your regular expression. I'll pay closer attention at work today and see how much of my stories is bold.

    Now i'll give you a little tip of my own. When searching for dates i've found it's best to use a date that isn't too close to today's date. Using May 2009 introduces a possibility that it's 'working by coincidence' just because it happens to be May 2009 at the moment. More than once i've had the experience where something seemed to be working but actually wasn't, and you might only find out when it becomes June 2009. By which time it might be harder to debug.

    Not saying there's any bugs in your code, just that you could play it safe by testing with different dates.

    Sorry if i'm not explaining myself very well. I only just woke up, lol!

    Really enjoying your series very much.

  2. You are correct, of course, Cucumber is only marking RexExps in bold. That doesn't stop me from assigning deeper meaning :)

    Very good point on the dates.

    My rule of thumb with dates is that I always start with today's date, that way I don't have to think about it. If I *need* to think about it (e.g. the feature is date driven), then I try to be more deliberate in choosing a good sample set of dates: today, yesterday, 1 week ago, 1 month ago, 1 year ago, and a leap day for good measure.

    My testing could be a bit more thorough in this case. I explicitly test back into April in this scenario. I should have tested back into 2008 while I was at it. Something to work on today :)

    Thanks for the feedback. I'm glad you're enjoying the series!

  3. Aha, that makes sense. Now that i am not so sleepy i see the hard coding in the original steps.

    By the way, you come up first on a search for "cucumber recipe" :)