Dr Nic

Cucumber: building a better World (object)

How to write helper libraries for your Cucumber step definitions and how to upgrade your support libraries from Cucumber 0.2 to 0.3 (released today).

In cucumber, each scenario step in a .feature file matches to a Given, When, Then step definition. The step definitions are normal Ruby code. First class, bonnified, honky-tonk Ruby code. And what’s the one thing we love to do to Ruby code on a rainy Sunday afternoon? Refactor it. Turn messy code into readable “return in 50 years, on the time capsule, and get back to work quickly” code.

In Cucumber we use a special, until-now unknown, magic technique for refactoring step definitions. They are called “Ruby methods”. Oooh, feel the magic. You take some code in a step definition and you refactor it into a method. And you’re done. For example:

When /I fill in the Account form/ do
  fill_in("account_name", :with => "Mocra")
  fill_in("account_abn", :with => "12 345 678 901")
  click_button("Submit")
end

Could be refactored into:

When /I fill in the Account form/ do
  fill_in_account_form
end

def fill_in_account_form
  fill_in("account_name", :with => "Mocra")
  fill_in("account_abn", :with => "12 345 678 901")
  click_button("Submit")
end

Good work. Or is it? No, we’ve done something a little naughty. We’ve polluted the global object space with our method and turns out it just isn’t necessary. There’s a nicer way and a clean idiom for how/where to write helper methods.

Annoyingly, that idiom broke with the release of Cucumber 0.3. So I’ll introduce both so you can fix any bugs that you spot and know how to fix them.

The solution is to understand the existence of the World object and the clean technique for writing features/support/foobar_helpers.rb libraries of helper methods.

Introducing the World object

To ensure that each cucumber scenario starts with a clean slate, your scenarios are run upon a blank Object.new object. Or in a Rails project its a new Rails test session ActionController::IntegrationTest.

These are called World objects (see cucumber wiki). You pass in a specific World object you’d like to use, else it defaults to Object.new For a Rails project, you’re using Cucumber::Rails::World.new for your world object each time, which is a subclass of ActionController::IntegrationTest.

The benefit of a World object starting point for each scenario is that you can add methods to it, that won’t affect the rest of the Ruby world you live in: which will be the Cucumber runner. That is, you cannot accidently blow up Cucumber.

Extending the World object

Step 1, put methods in a module. Step 2, add the module to your World object.

So that we’re all extending Cucumber in the same way, there is a folder for where your helper methods should be stored, and a programming idiomatic way to do it. It has changed slight from Cucumber 0.2 to 0.3 so I’ll show both.

For our helper method fill_in_account_form above:

  1. Create features/support/form_submission_helpers.rb (its automatically loaded)
  2. Wrap the helper method in a module module FormSubmissionHelpers ... end
  3. Tell Cucumber to include the module into each World object for each Scenario

In Cucumber 0.3+ your complete helper file would look like:

module FormSubmissionHelpers
  def fill_in_account_form
    fill_in("account_name", :with => "Mocra")
    fill_in("account_abn", :with => "12 345 678 901")
    click_button("Submit")
  end
end
World(FormSubmissionHelpers)

For Cucumber 0.2 your complete helper file might have looked like:

module FormSubmissionHelpers
  def fill_in_account_form
    fill_in("account_name", :with => "Mocra")
    fill_in("account_abn", :with => "12 345 678 901")
    click_button("Submit")
  end
end
World do |world|
  world.extend FormSubmissionHelpers
end

Where the difference is the last part of the file. This mechanism is deprecated and results in the following error message after upgrading to Cucumber 0.3:

/Library/Ruby/Gems/1.8/gems/cucumber-0.3.0/bin/../lib/cucumber/step_mother.rb:189:in `World': You can only pass a proc to #World once, but it's happening (Cucumber::MultipleWorld)
in 2 places:

vendor/plugins/cucumber/lib/cucumber/rails/world.rb:72:in `World'
vendor/plugins/email-spec/lib/email_spec/cucumber.rb:18:in `World'

Summary

Refactor step definitions. Put it in features/support/…_helpers.rb files, inside modules that are assigned to the World object.

And a word from our sponsor

Starting a new Rails project and need the team that is up-to-date with all the latest and greatest gadgetry, plugins and gems, styles and processes for enterprise and web2.0 web applications? Ask us at Mocra.

Need professionals to help your Rails project burst over the finish line? Ask us at Mocra.

Easy scheduling by location, tasks and people – a case study of a client application from Mocra

UPDATE: Orchestrate was reported in TechCrunch

Several years ago Andy Wright uncovered a problem. Small and large businesses can have the same problem: orchestrating to get a qualified person to perform a job. Who’s got the skills? Who’s available? What tasks have you offered/sold to your customers?

Its hard enough getting customers and finding great staff/contractors, but then you might spend many nervous hours juggling demand against availability. Then you need to keep a history of jobs and tasks performed. Did your staff do the work? Does anyone need to follow up?

The same types of jobs, the same set of qualified staff, the same process of orchestration. Every day. Bah. Fortunately Andy invested the last several years into solution towards automation: Orchestrate.

I’m in love with this application because Mocranites Bo Jeanes, Ryan Bigg, Jack Chen and myself helped it mature over the last 6 months into a production-ready, enterprise scaling mega app. From adolescence to adulthood.

Orchestrate has now graduated “Rails Development University” and gone live, after a two month stint in beta.

The design for Orchestrate was done by Jon Hicks, world famous as the creator of the FireFox logo. The original code base was created by Jon Leighton. Both Jons were brilliant to work with and we hope to run into them again on future projects. More recently, the marketing/main site of Orchestrate was developed by Ryan Carson and his team at Carsonified.

Andy has brought together some of the best developers and designers in the world for this project, and it is so wonderful that at Mocra we can now proudly point to Orchestrate as one of its growing number of world-class client projects.

Here’s hoping for a TechCrunch write up… ! (Yay it came true!)

Polish your Rails project with Mocra

I want to help you, your business, your boss and your project reach delightful levels of wickedly awesomeness. I’m so proud of the small team of ace Rails developers here at Mocra and what I know we can do for you. Orchestrate is delicious proof of pudding.

Send an email to rails@mocra.com about your current/future projects. Its free to ask for help and I’ll even throw in the answers for free. Dare us to be more awesome!

While you wait for a reply perhaps learn more about How we do it?