At work, I’ve recently been downplaying the importance of Gibbon being distributed as a library, since I don’t really think it’s the code that’s important… the technique is where it’s at. As such, I’m not going to give the hack itself or its configuration any stage time in order to jump in and talk about how to use it and what its limitations are.
By and large, Gibbon’s syntax apes Cucumber’s take on Given/When/Then Templates.
There are, however, some gotcha’s and limitations and that’s what I’d like to focus on. Let’s start off with an example:
Feature "A Tomatoist does a pomodoro" do
Story <<-eos
In order to perform a focused unit of work
As a Tomatoist
I want to start a pomodoro
eos
Scenario "Starting a pomodoro" do
When "I go to the home page" do
executes { visit '/' }
Then "I should be sent to a new session" do
current_url.should =~ /\/\w{3,}/
end
And "I should see an unstarted timer" do
response.should have_tag('#timer .countdown_row','00:00')
end
And "I should see a pomdoro button" do
response.should have_tag('input[type=submit][value=?]','Pomdoro')
end
When "I click the pomodoro button" do
executes do
@session_url = current_url
click_button 'Pomodoro'
end
Then "I should be on my session's page" do
current_url.should == @session_url
end
And "my timers should have been initialized" do
response.should have_tag('#timer .countdown_row','25:00')
end
And "my timer history should show the current pomdoro" do
response.should have_tag('#history ul li', /Pomodoro/, 1)
end
And "the pomodoro button should be highlighted" do
response.should have_tag('form.current input[type=submit][value=?]','Pomdoro')
end
end
end
end
end
And here’s what the output of that feature looks like using RSpec’s built in nested formatter:
A Tomatoist does a pomodoro
In order to perform a focused unit of work
As a Tomatoist
I want to start a pomodoro
Scenario: Starting a pomodoro
When I go to the home page
Then I should be sent to a new session
And I should see an unstarted timer
And I should see a pomdoro button
When I click the pomodoro button
Then I should be on my session's page
And my timers should have been initialized
And my timer history should show the current pomdoro
And the pomodoro button should be highlighted
There are a couple of things you should note about the syntax straight off:
It’s also noteworthy that the primary means of interrogating the response is through either the have_tag matchers or using something like contain to look for text. This is not necessarily a bad thing, but it can lead quickly to close coupling unless you are constantly vigilant. I’m still mulling this aspect over, but as it stands, I haven’t thought of a better way to handle it.
I’ve talked some to Tim Pope about Gibbon, since he’s a big fan of Cucumber driven development. His criticism, in a nutshell, is that the English text is not executable. He asserts that Cucumber keeps you honest by actually mapping the text to executable code. This is definitely valid; I frequently make the same argument about comments: “they’re lies waiting to happen”. It’s only a matter of time ‘til they become stale and the code itself is more recent. I would argue though, that Cucumber only separates you from that same fate by virtue of the author’s diligence.
Tackling the problem of example descriptions diverging from implementation from the opposite direction is Jim Weirich’s recent entry into this field, Given. Given eshew’s explanatory text altogether, preferring to let the code speak for itself. Now we’re talking! Combine that with something like Stories’ ability to convert code to plain English for output and you’d have a really nice offering. What this solution still lacks though is the facility of RSpec’s matchers, as Given is built on top of Test::Unit (as is Stories).
For now, I’ll be sticking with Gibbon, since it provides me access to RSpec’s matchers, is crazy simple and gives me pretty much everything I require:
I’d love to hear from you in the comments about what your experience is with integration testing in general and how you’re scratching that itch these days.