Posts Tagged ‘BDD’

Writing a Cucumber Feature

January 2nd, 2010

This is the second post about Cucumber, where we’ll be exploring the creation of a feature. Cucumber centers on testing the expected behavior of your application, rather than the expected behavior of a method. I had someone explain to me once, “unit tests/specs are the methods/implementation you wish you had, Cucumber tests are the application you wish you had.”

User Stories

When thinking about a Cucumber feature you will want to relate it to a user story. If you’re familiar with Agile development stories are nothing new.  For those who are not though it’s not too complicated. A story is simply an expression of value generally in the form of:  ”As {a role} I want/need {to do some function} so that {business value}.” Generally the business value is best expressed in terms of: revenue generation, revenue protection, operational efficiency (cost savings).

For the example we’ll be using this story:

As a workshop co-ordinator I need to manage contact information so I can easily reference individuals and start communications with them.

You’ll notice that this story doesn’t imply implementation.  You generally never want to imply implementation in a story.  Also you’ll notice that the story is simple and comprehensible by pretty much anyone (business user, developer, average Joe Public).

Starting a Feature File

In our Rails application we’ll want to go inside our /features folder.  Create a new file called manage_contact.feature.  At the top of the file type in the following:

Feature:  Manage Contact
  In order to easily reference individuals and start communications with them
  As a workshop co-ordinator
  I want to manage contact information

Now we have our first feature.  The next step is to define some scenarios.  Scenarios express various actions the user might take in relation to the feature.  Below are some of the scenarios we came up with for this feature (these go below your feature entry – indented to be even to the “In order, As a, I want” section).  You don’t need to, but I like listing the scenarios in order of priority, this way a developer working on the feature can just start on top and work downwards:

Scenario: View list of contacts

Scenario: Add a new contact

Scenario: Email a contact from the list

Scenario: Remove an existing contact

Scenario: Update an existing contact

Scenario: Start an IM conversation with a contact from the list

Your finished feature file should look something like this gist. In the next post we’ll start to look at building out our scenario’s with step definitions, that is define what behavior we expect to happen with each scenario we have defined.

Adding Cucumber to Your Salad

January 2nd, 2010

Cucumber has come a long way since I’ve first started to use it.  My two posts about getting started with BDD have been the most visited pages of my blog.  So it seems only fitting to address the new features and update the material.  I’ll be creating a series of blog posts (so it doesn’t end up being one giant scrolling page) addressing a real-life situation of creating a contact management feature.  We’ll build it from the ground up using Cucumber and RSpec.

Installing Cucumber / RSpec

The first step to using Cucumber (and RSpec) is to install the necessary gems.  This is very straightforward and probably familiar to most people:

sudo gem install rspec rspec-rails cucumber cucumber-rails webrat ZenTest database_cleaner

If you are using Ruby > 1.9 you will also need to install the test-unit gem (or RSpec will not load properly).

sudo gem install test-unit -v=1.2.3

Now if you run into errors installing webrat, know that it requires nokogiri (an awesome XML parser, see my blog post about nokogiri).  In order to build nokogiri though you need to have a few libraries installed ahead of time.  If you run into any errors installing webrat (likely because it couldn’t install the dependency nokogiri gem) try the following (I’m using a Debian -Kbuntu- flavor of Linux, so your specific command might vary):

sudo aptitude install libxml2-dev libxslt1-dev

Once you have installed the two developer libraries for libxml2 and libxslt you can try to reinstall webrat (sudo gem install webrat) and it should properly build.

Adding RSpec and Cucumber to a Rails Project

Having the gems installed is the first step.  To use Cucumber in your Rails project you need to run a few generation scripts which prime your application and make a few config updates. Run the following commands from inside your Rails application root:

script/generate rspec
script/generate cucumber

These scripts create new rake tasks, a spec and feature folder, as well as modify your database.yml (adding a new cucumber environment).  You’ll also notice a new cucumber environment file added to your environments folder already completed with all the necessary config.gem entries, nice.

That’s all there is to it for getting started.  Next post I will talk about outside-in development and starting your first feature!

May 2009 Cucumber Presentation

August 2nd, 2009
Below is a presentation I gave at Austin on Rails.  You can also find the audio for it here:  nicholas-cucumber-rails-audio
View more documents from ncancelliere.

Railscasts has video on Cucumber

March 31st, 2009

Ryan Bates, at Railscasts, has posted a great movie that takes you though using cucumber step-by-step. As are all his video pod-casts, it’s good stuff.

You can find it at this url: http://railscasts.com/episodes/155-beginning-with-cucumber

BDD Step-by-Step Example (part 2, refactor)

March 29th, 2009

The cool thing about Cucumber is how it allows you to reuse your step definitions. It allows you to effectively create a DSL whereby you can generate new tests with little or no additional coding. As you’re writing step definitions keep this in mind, and look for patterns that you can dry up.

Below is an example of a step definition that has grown over time. These steps support a user administration feature using restful_authentication and aasm_state.

Given /^a user that is pending$/ do
  pending
end

When /^I activate the user$/ do
  pending
end

Then /^the user should be able to log in$/ do
  pending
end

Given /^a user that is active$/ do
  pending
end

When /^I suspend the user$/ do
  pending
end

Then /^the user should be in a suspended state$/ do
  pending
end

Then /^the user should not be able to log in$/ do
  pending
end

Given /^a user that is suspended$/ do
  pending
end

When /^I unsuspend the user$/ do
  pending
end

Then /^the user should be in a pending state$/ do
  pending
end

When /^I delete the user$/ do
  pending
end

Immediately you should recognize that there are some patterns here: given something about the user’s state, and when the admin does something to the state, and whether or not you can log in.

Let’s see if we can’t boil these down these 20 some steps into just 4-5? First lets aggregate the common Given’s, using the power of regular expression we can do something like this:

Given /^a user that is (.+)$/ do
end

Hmm, interesting — this might work, so lets try finishing it up:

Given /^a user that is (.+)$/ do |state|
  @user = User.create!(:login => 'testuser', :email => 'test@test.gov', :password => 'testme', :password_confirmation => 'testme', :state =>state)
end

Odd, the test is failing still. Well if you understand how restful_authentication works with aasm you’ll soon discover that any user created on the back-end starts out in a passive state. This actually is going to take some thought, so lets come back to it.

How about all the “When I” steps? Those are easily grouped together. Since restful_auth uses nice verbs for state transitions we can leverage this in our step definitions:

When /^I (.+) the user$/ do |state|
  @user.send("#{state}!")
end

Along the same line of thinking we can apply this to our “Then the user” steps:

Then /^the user should not be able to log in$/ do
  visit login_path
  fill_in "login", :with => @user.login
  fill_in "password", :with => @password
  click_button "Log In"
  response.should contain("Couldn't log you in")
end

Then /^the user should be able to log in$/ do
  visit login_path
  fill_in "login", :with => @user.login
  fill_in "password", :with => @password
  click_button "Log In"
  response.should contain("Logged in successfully")
end

Then /^the user should be in a (.+) state$/ do |state|
  @user.send("#{state}?")
end

There’s a lot going on in the log-in steps, we take the same exact steps to log in a user, the only change is the outcome we expect. Ok so lets refactor that out into a method, and we get:

Then /^the user should not be able to log in$/ do
  login_using_form("Couldn't log you in")
end

Then /^the user should be able to log in$/ do
  login_using_form("Logged in successfully")
end

def login_using_form(expectation)
  visit login_path
  fill_in "login", :with => @user.login
  fill_in "password", :with => @password
  click_button "Log In"
  response.should contain(expectation)
end

Ok, better, so what about the given a user? Anything we create on the backend will start out in a state of passive (short of changing how restful_auth works). Also not all the verbs we would use in a step definition equate to a valid transition in aasm (you cannot transition to passive or pending directly). Well it takes a little more thought, but in the end you might end up with a method like the one below:

Given /^a user that is (.+)$/ do |state|
  state_hash = { :active => "activate!", :suspended => "suspend!" }
  @password = 'testme'
  @user = User.create!(:login => 'testuser', :email => 'test@test.gov', :password => @password, :password_confirmation => @password)
  ##
  # Restful_Authentication doesn't provide direct state transistions for passive or pending,
  # so we do a little tweaking to our user object to get it into the desired state.
  #
  @user.register! unless state == 'passive'  # new accounts created through backend start out as passive
  unless state == 'pending'                  # accounts when registered become pending
    @user.send(state_hash[state.to_sym])     # pending can transition easily to any state
  end
end

Putting it all together we have a step definition file now of only 5 definitions and one helper method. These steps can support a wide combination of user starting states, user state transitions and expectations around logging in and user state post transition. Let the business folks go hog wild!

Given /^a user that is (.+)$/ do |state|
  state_hash = { :active => "activate!", :suspended => "suspend!" }
  @password = 'testme'
  @user = User.create!(:login => 'testuser', :email => 'test@test.gov', :password => @password, :password_confirmation => @password)
  ##
  # Restful_Authentication doesn't provide direct state transistions for passive or pending,
  # so we do a little tweaking to our user object to get it into the desired state.
  #
  @user.register! unless state == 'passive'  # new accounts created through backend start out as passive
  unless state == 'pending'                  # accounts when registered become pending
    @user.send(state_hash[state.to_sym])     # pending can transition easily to any state
  end
end

When /^I (.+) the user$/ do |state|
  @user.send("#{state}!")
end

Then /^the user should not be able to log in$/ do
  login_using_form("Couldn't log you in")
end

Then /^the user should be able to log in$/ do
  login_using_form("Logged in successfully")
end

Then /^the user should be in a (.+) state$/ do |state|
  @user.send("#{state}?")
end

# step helpers --------------------------------------#

# this method simulates a login
def login_using_form(expectation)
  visit login_path
  fill_in "login", :with => @user.login
  fill_in "password", :with => @password
  click_button "Log In"
  response.should contain(expectation)
end

A real-life feature that these steps support:

Feature:  User Administration

So that I can control access to the application
As an admin
I want to manage users

Scenario: Activate a pending user
  Given a user that is pending
  When I activate the user
  Then the user should be able to log in

Scenario: Suspend a user that is active
  Given a user that is active
  When I suspend the user
  Then the user should not be able to log in

Scenario: Unsuspend a user that is suspended
  Given a user that is suspended
  When I unsuspend the user
  Then the user should be in a pending state
  And the user should not be able to log in

Scenario: Purge a user that is active
  Given a user that is active
  When I delete the user
  Then the user should not be able to log in

Scenario: Purge a user that is pending
  Given a user that is pending
  When I delete the user
  Then the user should not be able to log in

Scenario: Purge a user that is suspended
  Given a user that is suspended
  When I delete the user
  Then the user should not be able to log in

BDD Step-by-Step Example (part 1)

March 29th, 2009

Before we can begin doing BDD (behavior driven development) we need to install some tools to help.

sudo gem install cucumber rspec-rails webrat

Next I go to my Rails application and run some scripts to get the basic set-up inside my rails directory structure:

script/generate rspec
script/generate cucumber

Now in the newly created features folder I will create a file called user_administration.feature. This file will contain all my scenarios for this feature. After spending some time thinking about how to express my features I come up with two basic ones to start:

Feature:  User Administration

So that I can control access to the application
As an admin
I want to manage users

Scenario: Create a new user account
  Given no user "joetest"
  When I create a new user "joetest" with password "skymonkey"
  Then the user "joetest" can log in with password "skymonkey"

Scenario: Suspend an existing user account
  Given a user that is active
  When I suspend the user
  Then the user can not log in

Now I run the following command to have cucumber generate snippet methods that I can then copy into a step definition.

script/cucumber features/user_administration.feature

I create a new file named user_steps.rb in the features/step_definitions folder. Now that we have our step definition snippets the next job is to take them from “pending” into something meaningful. We’ll start with the first scenario. After giving it some thought it might end up looking like this:

Given /^no user (.+)$/ do |login|
  lambda{ User.find(login) }.should raise_error(ActiveRecord::RecordNotFound)
end

This first part sets us up. It allows us to express the given state, in this case not finding a user named “joetest”.

When /^I create a new user "(.+)" with password "(.+)"$/ do |login, password|
  visit new_user_path
  fill_in "user_login", :with => login
  fill_in "user_email", :with => "#{login}@test.org"
  fill_in "user_password", :with => password
  fill_in "user_password_confirmation", :with => password
  click_button "Sign Up"
end

Next we describe the action we’ll be taking. This code simulate the submission of a form, using Webrat. We take this approach because it makes us exercise the whole stack views, controller and models. It’s just short of me going to the site with a browser.

Then /^the user "(.+)" can log in with password "(.+)"$/ do |login, password|
  visit login_path
  fill_in "login", :with => login
  fill_in "password", :with => password
  click_button "Log In"
  response.should contain("Logged in Successfully")
end

Lastly we test our outcome, that the user the admin created is able to log in. So the end result is test that describes the behavior of an admin creating a new account for a user, and they are able to successfully log in.

Along the way you may find tests go red (fail), this is normal. You want to write code (just enough) to make them go green, then continue to write more test code. Keep repeating this until all your tests run green.

Also the nice thing about step definitions is that they can be reused in other tests. We’ll likely have other scenarios where we create a new user or try to log in as one. Our code stays dry and we (the developer) stay confident. (Now it’s sounding like a anti-perspirant commercial, so I’ll stop now.)

Going Outside In with Rails, Cucumber, and RSpec

March 28th, 2009

Traditionally when you do emergent design, at least in my experience, you go from the inside out. Work from your model into your controller and views. Since web applications are so heavily focused on user interaction it sometimes is better to work from the outside in. When working with a Rails application think instead of your views and work into your controller and models.

Understand the UI first before you write any code. Use things like paper prototyping to understand real user data needs and challenges. Paper prototyping is a cheap and easy way to understand what objects and data a user will be interacting with for a given task. Keep your session short, and focus only on the feature you’re developing. You can find examples of paper prototyping on YouTube.

Get boot-strapped with Cucumber, RSpec and RSpec-Rails, and dive straight into BDD on your Rails project. To do this you need to install various gems to support your development efforts:

sudo gem install cucumber rspec-rails webrat
cd my_rails_project
script/generate cucumber
script/generate rspec

That’s all you have to do to get going! You’ll notice that you’ll have a new features folder and the usual spec folder (from RSpec). You will want to describe your features as stories and scenarios for Cucumber in the features folder. Start with your views when you think about scenarios.

The basic gist of it is just like regular TDD, except we are focusing on behavior:

  1. Choose a single Cucumber scenario
  2. Create a failing step definition in Cucumber
  3. Create a failing spec in RSpec
  4. Write code to make the spec pass (red/green refactor)
  5. Repeat 3 and 4 until the scenario is passing

In the process of making the scenario pass you’ll find yourself discovering models and controllers that will need to be written or updated. Taking this type of approach helps you focus on writing just enough code to get your tests passing, without over engineering, and has the added benefit of you writing testable code. (Remember TDD/BDD is not so much about testing as it is about communication, documentation and design!)

Your finished scenario should touch a complete vertical slice of your application, that is to say, model->controller->view. It should provide some real value to the user. Agile development philosophies frown on partial feature delivery. A schema, security filter or screen alone is not a feature. If you write scenarios from the user’s perspective you’ll avoid this pitfall.

To learn more about Cucumber and RSpec (either in Rails, or plain Ruby) I would recommend checking out Pragmatic Bookshelf, they have a beta book on RSpec.