Dr Nic

hash bang cucumber

I don’t know if this is a good idea or not, but often I just wish I could copy and paste a cucumber feature file into the command line and have it just run the frigging scenarios without having to prefix it with “cucumber”.

running scenarios directly from command line

Perhaps I’m a bit delirious but I think it would be fun to paste features/112_users_crud.feature into the console and it would run the scenarios:

$ features/users_crud.feature

instead of having to always do the extra key strokes:

$ cucumber features/users_crud.feature

Solution?

Two steps:

  1. At the very top of each feature file add: #!/usr/bin/env cucumber
  2. Run chmod +x features/*.feature

Shazam!

Hash bang me up!

To apply this to all your feature files, jump into script/console or irb and run the following code within your project:

Dir["**/*.feature"].each do |feature|
  contents = File.read(feature)
  File.open(feature, "w") do |f|
    f << "#!/usr/bin/env cucumber\n\n"
    f << contents
  end
  `chmod +x "#{feature}"`
end

FutureRuby talk: Living With 1000 Open Source Projects

The FutureRuby conference has been (and still is, as of 11:43am on Sunday) wonderful. I just finished my talk on “Living With 1000 Open Source Projects” which was great fun, good for a bunch of laughs, and more importantly allowed me to share some thoughts on Zero Maintenance, Managing community expectations, self-sustaining communities, and the difficulty of scaling pet children.

Below are the slides and all the nice things people said about the talk, which has made me feel very good for sharing, and for the 60hr return flight from Brisbane to Toronto.

If you want to hear the jokes, and an Australian “mistaking” Canada for a state of America, perhaps wait for InfoQ to publish the video.

The slides

Nice things people said

@drnic #futureruby

Thanks to…

Elle Meredith who helped me design and theme the slides so they looked spot-on-awesome.

My two children for not being old enough to be disturbed by some of the things I said about them during the talk.

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.

Testing outbound emails with Cucumber

My testimonial for Cucumber still stands even in 2009. In fact I promise to let you know when I don’t think Cucumber is the bees-knees of integration testing. I love the step-by-step English instructions of user usage scenarios backed by a simple Ruby DSL for describing real actions on your application for each step.

For testing Rails apps, Cucumber defaults to using Webrat on top of Rails’ own integration sessions. With Webrat you actually test that your views match to your controller actions. If I click the “Submit” button, then it checks that it invokes an available action correctly. Before Cucumber I could appreciate the merit of Webrat alone, but the two tools combined are instant superheroes of my Rails development crime fighting team. That is, like any superhero TV show at least once every 30 minutes you find yourself saying “Thank you Mr Cucumber and your trusty sidekick Webrat, you’re my heroes”. (BTW, I’m not alone in this analogy)

Testing emails

But I wanted to test emails. More interestingly, emails containing links back into my application. Like an activation email on sign up.

Specifically, I wanted a cucumber scenario like this:

Scenario: Signup for new account
  Given I am on the signup form
  When I fill in "Email" with "drnic@mocra.com"
  And I press "Join"
  Then I should see "An activation email has been sent"
  And I should receive an activation email
  When I click the activation email link
  Then I should see "Your account is active"

Within this scenario there are 7 steps. Lines 2, 3, 4 and 7 match to steps from the generated webrat_steps.rb file when you install cucumber.

Line 1 also matches to a webrat step definition. But it requires that you define what “the signup form” maps to in your routes. So you need to update features/support/paths.rb to specify what “the signup form” url is:

module NavigationHelpers
  def path_to(page_name)
    case page_name
    when /the signup form/
      signup_path
    end
  end
end

So, that leaves us with lines 5 and 6.

email-spec plugin

I’m happy using the email-spec gem/plugin from Ben Mabey, who is also the lead maintainer of the awesome Cucumber TextMate bundle and did a wonderful presentation on Outside-In Development with Cucumber at the recent Mountain West RubyConf (lots of great videos available)

To install as a plugin:

script/plugin install git://github.com/bmabey/email-spec.git

Then add the following line to your *feature/support/env.rb* file:

require 'email_spec/cucumber'

Finally, the plugin comes with some bonus cucumber step definitions which wrap around lots of nice helpers:

script/generate email_spec

Using email-spec step definitions

Let’s ignore my desired lines 5 and 6 above and use the step definitions that we get with email-spec. We can replace the two lines with the following:

And I should receive an email
When I open the email
Then I should see "Please activate your new account" in the subject
When I click the first link in the email

Update: The last line didn’t used to be in email-spec but it now is. I’ve removed the example from this article.

I did all this for an existing application and every line of the scenario tested positive/green. Yay!

Refactoring four steps into two

For the sake of demonstration, you now might want to refactor these four steps into two steps to keep your scenarios nice and readable.

That is, how can we convert these 4 steps into our original lines 5 and 6?

The quickest way is to copy and paste the lines and slap some quotes around the text:

Then /^I should receive an activation email$/ do |email|
  Then 'I should receive an email'
  When 'I open the email'
  Then 'I should see "Please activate your new account" in the subject'
end

When /^I click the activation email link$/ do
  When 'I click the first link in the email'
end

That’s right, you can use Given/When/Then as invocation methods as well as step definition methods. If you don’t pass these methods a do…end block then they will match/find/invoke a step rather than define a new one. Very cool.

Alternately, you could implement the two steps using the underlying email helper methods provided by the email-spec plugin:

Then /^I should receive an activation email$/ do
  unread_emails_for(current_email_address).size.should == 1
  open_email(current_email_address)
  current_email.should have_subject(/Please activate your new account/))
end

When /^I click the activation email link$/ do
  click_first_link_in_email
end

Your choice.

Configuring current_email_address

All the email-spec helper methods assume that the “I” in the scenario has an email address. It uses a method current_email_address for this.

You must change the current_email_address method in the email_steps.rb file to pull out email addresses from wherever they might be located within any given scenario.

For example, here is one definition of the method from one of my projects:

def current_email_address
  @email || (@current_user && @current_user.email) || "drnic@mocra.com"
end

Summary

With Cucumber and email-spec it is very easy to send and test emails. So my thanks go to Ben Mabey for this plugin.

Future proofing your Ruby code. Ruby 1.9.1 is coming.

Bugger.

I’m a Ruby monogamist. I use the Ruby that comes with Leopard (ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]). Oh sure I’ve cheated on my Ruby a couple of times. It was just sex, I didn’t fall in love, I promise.

My machine has had various versions of jRuby, Rubinius and MacRuby installed at various times, but I don’t think it’s ever had two working copies of Ruby MRI (Matz Ruby Implementation) at a time.

Ok, that’s a lie. On Christmas Day 2007, Matz released Ruby 1.9. Yes I was a deviant. I installed it. But I didn’t inhale. ruby19 sat on my filesystem outside the $PATH. It was a trophy not a tool.

My Ruby monogamy was working out perfectly for me, whilst I collected futurist and novelty Ruby implementations as a passive hobby, until yesterday when I saw the above tweet.

That’s a bit of a bugger. I mean, why was he playing with Ruby 1.9.1 anyway?

Or perhaps having multiple version of Ruby on one computer isn’t some illicit activity. Everyone else is doing it, Your Honour.

I still didn’t care for having multiple ruby versions lying around in my path; but I had to fix rubigen. I just wanted a way to fix bugs against ruby 1.9+ and move on with my life. I didn’t want to have to do any manual work in order to set this up. Surely someone else has solved this problem already?

(more…)