Refactoring Cucumber

17 December 2012

At work, we’ve been moving away from Cucumber tests for the past number of months, in favor of the more robust Capybara and rspec paring. Given that I’m a relative novice in Rails test-driven development, the supposed brittleness of Cucumber tests wasn’t immediately clear to me, so I did some digging, and came up with a few posts that talk about the pitfalls of the “imperative” style of step writing for user stories. A solid example of this is Ben Mabey’s ancient, but still applicable post, “Imperative vs. Declarative Scenarios in User Stories”. Mabey’s assertion is that it is too easy to rely too heavily on the built-in Cucumber web steps. For example:

When I fill in Name with 'Alligator'
And select Phylum as 'Chordata'
And fill in Animal Class with 'Sauropsida'
And fill in Order with 'Crocodilia'
And fill in Family with 'Alligatoridae'
And fill in Genus with 'Alligator'
And check Lay Eggs
And click the Create button

Here, Maybe suggests, the “story” part of the user story tends to get lost and mired in the technical details. I was heavily influenced by this post, and decided to do a little Cucumber refactoring at work to try out Mabey’s declarative style, which he demonstrates thusly:

	Story: Animal Submission
	As a Zoologist
	I want to add a new animal to the site
	So that I can share my animal knowledge with the community
	
		Scenario: successful submission
		Given I'm on the animal creation page
		When I add a new animal
		Then I should see the page for my newly created animal
		And the notice 'Thank you for your animal submission!'

As Mabey notes, it has more of the token for conversation feel to it, and it’s immediately clear that it is more likely to meet the user’s need, since, in this case, the user cares more about completing their goals than how those goals are handled on the back end.

My Attempt

I took Mabey’s advice to our own codebase, and I extracted a Cucumber test that I had written a few months back. Now, I won’t start to bad mouth my code and talk endlessly about how terrible a person I am for having written it this way, but I’ll give you a taste of how poorly this test spoke to the user’s need. I present to you this feature test for our site’s feedback form:

	#features/feedback.feature
Feature: Feedback
Scenario: Submitting feedback
	Given I go to the feedback page
	And I fill out the feedback form and submit it	
	Then I should see a success message
	Then I should see "Email can't be blank"
	When I fill in "Your email" with "nick@navigatingcancer.com"
	And I fill in "Summary" with "This site rules"
	And I fill in "What you observed" with "This site rules"
	And I press "Send"
	Then the flash notice should be "Thank you for your feedback! |
		We try to respond to questions and requests within one business day."
	And ruby FeedbackForm.last.email.should == 'nick@navigatingcancer.com'
	And ruby FeedbackForm.last.content.should match "This site rules"

Yep, you read that right. That’s a Ruby function inside of a Cucumber test. I wonder what user will be thinking of their form submission in terms of a Ruby method chain? Now here’s my improved version, written with Turnip and Capybara.

#spec/acceptance/forms/feedback.feature'
Feature: Feedback form

	As a user of the site
I want to be able to submit feedback about the site
So that I can report if something breaks

	@feedback
Scenario: Submitting feedback (successful)
	Given I go to the feedback page
	And I fill out the feedback form and submit it
	Then I should see a success message

#spec/acceptance/forms/feedback_steps.rb
require 'spec_helper'
steps_for :feedback do
	step 'I go to the feedback page' do
  	page.visit '/feedback'
	end

		step 'I fill out the feedback form and submit it' do
 		page.click_button 'Send'
 		page.should have_content "Email can't be blank"
 		page.fill_in 'Your email', with: 'joe@navigatingcancer.com'
 		page.fill_in 'Summary', with: 'Just saying'      		page.fill_in 'What you observed', with: 'I am awesome'
  	page.click_button 'Send'
	end

		step 'I should see a success message' do
  	page.should have_content "Thank you for your feedback! We try to respond to questions and requests within one business day."
 		FeedbackForm.last.email.should == 'joe@navigatingcancer.com'
  	FeedbackForm.last.content.should match 'I am awesome'
	end
end

See how much more nicely that reads? Now the user story is really coherent. They go to the form and submit it, and they should get a success message. That’s truly how simple the step should be. Notice that I still handled the validation in Capybara. But the it’s not the user’s job to think about what should happen if they don’t submit the form correctly, it’s mine. Thus, I should handle that in the test logic that doesn’t involve the user story.

Onward

As a front-end developer coming to the TDD Rails world, Cucumber was helpful to me because it helped me to think about things from the browser’s perspective. The gherkin syntax, when used well, really does allow for expressive steps and user stories that invite collaboration with non-developers. But that syntax paired with Capybara’s more robust step handling make for a much clearer and well-executed test. Notably, the reason I went back to this test was that we had changed the UI for the form. Our previous form submitted an unfuddle ticket, but we have recently switched our ticketing system to Assembla. Also, we added a few fields and renamed others. In this case, the Cucumber test (previous to the one I have posted) would have failed because it wouldn’t have been able to find the form labels it was looking for. But the user story remained the same. All the user wants to do is submit a feedback form. So rather than changing several tests to reflect this, why not only change the ones that are concerned on the back end? They’re easier to maintain, and as great as Cucumber was, I find that I prefer the Capybara-rspec approach when it comes to looking for content on the page. I’m looking forward to exploring this technique across our codebase during refactoring, and in the future.

Fork me on GitHub