I don’t know if cucumber is a fruit or vegetable
There’s a lot of buzz in the community right now about the latest framework for top-down BDD: Cucumber . Cucumber lets you describe features in plain text, from a user’s perspective. Once you have your thoughts down in English (or another language), you can map each step of your spec to Ruby code. This perfectly complements our love of design-driven, outside-in development.
Let’s be assertive
Most of Cucumber’s users seem to be fans of RSpec, but if you’re using Test::Unit (or Shoulda), Cucumber works just as well. First, you’ll have to tell Cucumber not to use RSpec:
# at the bottom of features/support/env.rb
# Comment out the next two lines if you're not using
#RSpec's matchers (should / should_not) in your steps.
# require 'cucumber/rails/rspec'
# require 'webrat/rspec-rails'
Next, you have to replace the matchers in the generated steps to use assertions:
# At the bottom of features/step_definitions/webrat_steps.rb
Then /^I should see "(.*)"$/ do |text|
# response.body.should =~ /#{text}/m
assert_match /#{text}/m, @response.body
end
Then /^I should not see "(.*)"$/ do |text|
# response.body.should_not =~ /#{text}/m
assert_no_match /#{text}/m, @response.body
end
Then /^the "(.*)" checkbox should be checked$/ do |label|
# field_labeled(label).should be_checked
assert field_labeled(label).checked?
end
And that’s it!
Unauthorized cucumbers
Our freshly-released authentication framework,
Clearance, also has built-in
support for Cucumber. If you’re using Clearance, just run script/generate
clearance_features
and you’ll get step definitions for authentication, as well
as feature files for the generated Clearance controllers for users signing up,
in, and out.
Cucumber factories
If you’re using factory_bot in
your project, it’s much easier to implement Given
steps that create data:
Given /^I have created a post with a title of "(.*)"$/ do |title|
Factory(:post, :title => title, :author_id => session[:user_id])
end
You can get a little creative and implement wildcard steps for basic data:
Factory.factories.each do |name, factory|
Given /^an? #{name} exists with an? (.*) of "([^"]*)"$/ do |attr, value|
Factory(name, attr.gsub(' ', '_') => value)
end
end
That will give you implementation for steps like these, assuming you have post, author, and comment factories:
Given a post exists with a title of "Super Post"
Given an author exists with an email of "user@example.com"
Given a comment exists with a title of "Bad Comment"
If you need to get anymore complicated than that, you should probably write a custom step.
Adding Cucumber to your diet
We wanted to add some integration tests to an existing project, and we decided to use Cucumber. Although that does mean we won’t confidently have 100% coverage in our integration tests, they’re still nice to have. But how do you add features for existing code?
We’ve been writing a feature for every new client request on that project - for each user-created ticket we handle, we create a .feature file (and include the ticket number in the feature title), and write steps for that request. This means that we have acceptance tests for all new client requests on that project. This approach may seem a little strange, but it’s been helpful, and we’re very happy with it so far. We’ll likely take a different approach if we use Cucumber on a project from scratch.
Now you have no excuse if your projects aren’t doing any kind of top-down testing, so get out there and write some acceptance tests!