Tuesday, December 28, 2010

Random Cucumber/Ruby Things I Learned

Some random stuff I learned as a "seasoned" Java developer, working in Ruby and more specifically Cucumber for the first time this year, and not having a clue as to what I was doing. I realize some of this stuff will be mind numbingly obvious to "seasoned" Ruby devs, but thought it would be interesting to share regardless.

Some background. On my project some "legacy" Cucumber tests existed. Legacy, you ask? How can Cucumber tests be legacy? Simple, they were overly fragile, and violated DRY over and over again. Therefore, since my overall group has chosen Cucumber as the ATDD tool of choice (and by extension Gherkin as the Story card language of choice), it was a good idea to get in and refactor the tests. I had a good example in how to do this in the blog posts by Jeff Morgan, and I fortunately had someone who I could tap on the shoulder on a sister team in Joel Helbling. So with the goal of learning some Ruby in mind, and a copy of RubyMine I began tearing up the soil and planting cukes.

Firstly, when you're working with multiple versions of Ruby, in my case, JRuby and Ruby for Windows, you might need to modify your Gemfile by the platform. In my case, I needed JRuby to run the tests the same way as the legacy tests were running. JRuby drove Celerity (yay HtmlUnit!) tests on Hudson, while native Ruby drives Waitr/FireWatir browser driven versions of the same tests.

So, although it's very obvious after the fact, here's a clue...there is no Celerity for Ruby. HtmlUnit is a Java library, and Celerity is just a Waitr API based wrapper of it. So it only works on JRuby. I thought the Gemfile would just handle that, but I was very very wrong. Therefore, you have to do something like this:

platforms :jruby do
gem "celerity", "0.8.2"
gem "syntax"

if RUBY_PLATFORM =~ /mswin|mingw/ #Jruby no likey mingw
platforms :mswin, :mingw do
gem "watir", "1.6.7"
gem "watir-webdriver"
gem "win32console"
gem "win32-clipboard"

This works out pretty well, just don't check in your Gemfile.lock. Ideally your QA testers will be running the Watir versions, while CI will maintain sanity when they can't. If you don't have QA testers, then I suggest having two versions of the project open, with separate Gemfile.lock that represent each platform.

I'm certain there's a better way to do this...but I've not discovered it yet.

Another random thing. If you want to pass a parameter (like a table) from one Cucumber step definition to another, you use a comma at the end of the line and pass the parameter in.

In my case, I was developing tests that triggered various UI based errors on each page during a process to say retrieve a password.

So I had a step definition that said:

When /^I forget my password and enter the credentials$/ do |table|
# table is a |username|account_number|
#blah blah blah with username and account_number

But what do I do when I have passed this step and need to enter security questions? I want to go through the successful parts of this step to enter the next step. How do I do this?

When /^I forget my password and enter the credentials and answer the security questions$/ do |table|
# table is a |username|account_number|answer1|answer2|answer3|
When 'I forget my password and enter the credentials', table
# Do stuff to answer the security questions

See that comma at the end of the step definition call? It passes table to the next step and lets it grab username and account_number out of the hash. I couldn't find this anywhere in documentation, which means it's painfully obvious to Ruby folks, but it's not if you don't know it. I'm sure it's documented somewhere, but I've failed at finding it. But boy discovering that was quite useful.

One last thing that I can think of right now. For some reason the version of JRuby I have to use doesn't like to use rspec. So similarly, to the first problem, we need to do strange things to make JRuby happy in env.rb:

require 'rubygems'
if RUBY_PLATFORM =~ /java/ #JRuby is strange
require 'spec'
require 'rspec'

Hopefully these tips will be of some use to folks like myself who don't know enough Ruby to be dangerous...yet.