Introduction
Applying the Behavior Driven Development (BDD) methodology with a
Ruby-based BDD framework allows developers to write system requirements
in a Ruby format first and then write Ruby code for the particular
features of those requirements. Because the Ruby format is similar to
plain English, business analysts and the people gathering the
requirements can participate in the development process.
BDD mandates that developers write tests for each step of the
process, which leads to cleaner code. The BDD process begins with the
development team creating a scenario, for which they describe each
feature. The initial scenario inevitably fails. The team subsequently
defines each step (iterative test) and writes the application code for
each feature. They also refactor the code when the scenario passes each
step.
A few different Ruby-based BDD frameworks are available. RSpec, Cucumber, and Shoulda are the most popular. Let's see how RSpec works by using the use case of an employee information manager.
Installing RSpec and Creating Required Directories
The installation of RSpec is pretty straightforward. I just use the gem install command on the command line.
saurabh@home:~$ sudo gem install rspec
[sudo] password for saurabh:
**************************************************
Thank you for installing rspec-1.2.9
Please be sure to read History.rdoc and Upgrade.rdoc
for useful information about this release.
**************************************************
Successfully installed rspec-1.2.9
1 gem installed
Installing ri documentation for rspec-1.2.9...
Installing RDoc documentation for rspec-1.2.9...
Next, I make RSpec locally available to my Rails application and add
it as a plugin to my Rails. When I run this installation, the
RSpec-Rails plugin will be installed within the Vendor directory of the
Rails application.
saurabh@home:/media/S3A6128D005/Users/ongc/eim$ script/plugin install git://github.com/dchelimsky/rspec-rails.git
Initialized empty Git repository in /media/S3A6128D005/Users/ongc/eim/vendor/plugins/rspec-rails/.git/
remote: Counting objects: 218, done.
remote: Compressing objects: 100% (176/176), done.
remote: Total 218 (delta 15), reused 129 (delta 3)
Receiving objects: 100% (218/218), 87.67 KiB | 93 KiB/s, done.
Resolving deltas: 100% (15/15), done.
From git://github.com/dchelimsky/rspec-rails
* branch HEAD -> FETCH_HEAD
After installation, I am ready to create the directories required for spec with the following command:
saurabh@home:/media/S3A6128D005/Users/ongc/eim$ ruby script/generate rspec
Configuring rspec and rspec-rails gems in config/environments/test.rb ...
exists lib/tasks
create lib/tasks/rspec.rake
create script/autospec
create script/spec
create spec
create spec/rcov.opts
create spec/spec.opts
create spec/spec_helper.rb
Applying BDD with RSpec
Now, let's take on the scenario of collecting employee information
and define it using RSpec according to the BDD process. While
collecting employee details, we will also collect information regarding
their interests and hobbies. So, here is how we present the scenario:
- User goes to the hobby page.
- User enters a list of sports he/she likes.
- The user cannot leave any box blank.
Now that we have a description of the scenario, we can start writing
the model specs. Before that, however, we need to create the skeleton
code for the model spec.
saurabh@home:/media/S3A6128D005/Users/ongc/eim$ script/generate rspec_model Hobby
exists app/models/
create spec/models/
create spec/fixtures/
create app/models/hobby.rb
create spec/models/hobby_spec.rb
create spec/fixtures/hobbies.yml
exists db/migrate
create db/migrate/20100112073803_create_hobbies.rb
BDD helps to create context for the scenarios. Here's how our feature specification for hobbies looks like.
context "A user " do
setup do
@hobby = Hobby.new
end
specify "should be invalid without all boxes filled"
@hobby.should_not_be_valid
@hobby.sports = "sportname"
@hobby.should_be_valid
end
end
Now, we need to write the code for this feature in order to make it
pass. In order to do so, we require a hobby model and table. We add the
tables using migrations.
class CreateHobbies < ActiveRecord::Migration
def self.up
create_table :hobbies do |t|
t.column :sports, :string
t.timestamps
end
end
def self.down
drop_table :hobbies
end
end
When we look at our specs, we see that the sports field is a mandatory. Hence, we need to add a validation in the Hobby Model.
class Hobby < ActiveRecord::Base
validates_presence_of :sports
end
In order to run the tests, we need to create the test database and
set up the test environment correctly. After that, we can run the spec
we just defined.
saurabh@home:/media/S3A6128D005/Users/ongc/eim/spec/models$ spec hobby_spec.rb
F
1)
ActiveRecord::RecordInvalid in 'Hobby should create a new instance given valid attributes'
Validation failed: Sports can't be blank
./hobby_spec.rb:11:
Finished in 0.03787 seconds
1 example, 1 failure
The test fails because the Validation says sports can't be blank.
Voila! We've done it; the validations are running. Let's add some data
and pass the values to the test. We will run them again when the data
is specified and see if they pass or not.
Data is passed in RSpec through the spec_helper. In this case, the
test data needs to be a value for sports. So, we use spec_helper to
pass the value of our sport through to the spec under the
valid_attribute specification.
require 'spec_helper'
describe Hobby do
before(:each) do
@valid_attributes = {
:sports => 'soccer'
}
end
it "should create a new instance given valid attributes" do
Hobby.create!(@valid_attributes)
end
end
Now, we run the spec tests again and check.
saurabh@home:/media/S3A6128D005/Users/ongc/eim/spec/models$ spec hobby_spec.rb
.
Finished in 0.189719 seconds
1 example, 0 failures
The test passed! We have just finished writing a feature using RSpec.
Conclusion
Behavior Driven Development is an effective methodology that reduces
the time required for requirements gathering, because business analysts
write their specs in the rspec format and developers can write code to meet that specification. It also introduces better discipline and control over tests.
Original Article