Semaphore has been, and continues to be, developed using the behavior-driven development process. In this post, I will take you through the steps needed to set up our prefered BDD stack on a new Rails 4 application and explain why each tool is important.

Create the application

$ rails new myapp

Install RSpec

We prefer RSpec to other testing frameworks since we find it more natural and expressive. For example, I never find myself using the word “assert” in everyday talk, while I use the word “should” regularly. Also, RSpec’s language for writing tests and output is very readable and can serve as documentation.

Add rspec-rails gem to the development and test groups of your Gemfile.

group :development, :test do
  gem 'rspec-rails', '~> 2.0'
end

Install the gem:

$ bundle install

Bootstrap the app with RSpec:

$ rails generate rspec:install

Create the RSpec binstub. In short, the binstub will allow you to run RSpec with bin/rspec instead of bundle exec rspec:

$ bundle binstubs rspec-core

Install shoulda-matchers

shoulda-matchers lets us spec common Rails functionality, like validations and associations, with less code.

Add shoulda-matchers gem to the test group of your Gemfile:

group :test do
  gem 'shoulda-matchers'
end

Install the gem:

$ bundle install

Let’s take a look at a simple validation spec.

If you are validating the presence of post’s title:

class Post < ActiveRecord::Base
  validates :title, presence: true
end

without shoulda-matchers the spec might look something like the following:

require 'spec_helper'

describe Post do

  describe "title validation" do

    context "title is present" do

      before(:each) do
        @post = Post.new(title: "My first post")
      end

      it "does not add an error on the 'title' attribute" do
        @post.should have(0).error_on(:title)
      end

    end

    context "title is not present" do

      before(:each) do
        @post = Post.new
      end

      it "adds an error on the 'title' attribute" do
        @post.should have(1).error_on(:title)
      end

    end

  end

end

and with shoulda-matchers:

require 'spec_helper'

describe Post do
  it { should validate_presence_of(:title) }
end

Install Factory Girl

Factory Girl is “a library for setting up Ruby objects as test data” or more precisely it is a fixtures replacement.

Add factory_girl_rails gem to the development and test groups of your Gemfile:

group :development, :test do
  gem 'rspec-rails', '~> 2.0'
  gem 'factory_girl_rails'
end

Install the gem:

$ bundle install

Basically, Factory Girl will allow you to create objects that you need in your tests without providing a value for each required attribute. If you don’t provide a value for a required attribute Factory Girl will use a default value that you defined in factory’s definition.

Factory Girl also has a more pleasant system for defining record associations than when using fixtures.

Let’s define a post factory:

FactoryGirl.define do
  factory :post do
    title "My first post"
    content "Hello, behavior-driven development world!"
  end
end

Now, if both the title and content attributes are required to create a valid post, instead of writing in our spec something like the following:

require 'spec_helper'

describe Post do

  describe "creation" do

    context "valid attributes" do

      it "should be valid" do
        post = Post.new(title: "My first post", content: "Hello, behavior-driven development world!")

        post.should be_valid
      end

    end

    context "invalid attributes" do

      it "should not be valid" do
        post = Post.new(title: "My first post", content: "")

        post.should_not be_valid
      end

    end

  end

end

you can just write:

require 'spec_helper'

describe Post do

  describe "creation" do

    context "valid attributes" do

      it "should be valid" do
        post = FactoryGirl.build(:post)

        post.should be_valid
      end

    end

    context "invalid attributes" do

      it "should not be valid" do
        post = FactoryGirl.build(:post, title: "")

        post.should_not be_valid
      end

    end

  end

end

Make sure everything is connected and working

Create a Post model:

$ rails generate model Post title:string content:text

      invoke  active_record
      create    db/migrate/20130726125040_create_posts.rb
      create    app/models/post.rb
      invoke    rspec
      create      spec/models/post_spec.rb
      invoke      factory_girl
      create        spec/factories/posts.rb

Notice, the generator now also creates a model spec and a ‘posts’ factory. That’s the reason why we included the rspec-rails and factory_girl_rails gems in the development group of the Gemfile.

Update the spec to validate post’s title and content:

require 'spec_helper'

describe Post do

  it { should validate_presence_of(:title) }
  it { should ensure_length_of(:title).is_at_least(5) }
  it { should validate_presence_of(:content) }
  it { should ensure_length_of(:content).is_at_least(10) }

end

And update the Post model with validation definitions:

class Post < ActiveRecord::Base

  validates :title, presence: true, length: { minimum: 5  }
  validates :content, presence: true, length: { minimum: 10  }

end

Before running the spec make sure to apply the migration and prepare the test database by recreating it from db/schema.rb.

$ bundle exec rake db:migrate db:test:prepare

After running the spec you can see it pass:

$ bin/rspec spec/models/post_spec.rb

Install Cucumber

Cucumber helps us both focus on the feature-level and as a high-level integration testing tool.

Add cucumber-rails gem to the test group of the Gemfile.

group :test do
  gem 'shoulda-matchers'
  gem 'cucumber-rails', require: false
  gem 'database_cleaner'
end

You can also add the database_cleaner gem which is not required, but it will save you a lot of heartache. It’s used to ensure a clean database state for testing.

Install the gems:

$ bundle install

Bootstrap the app with Cucumber:

$ rails generate cucumber:install

Create the Cucumber binstub:

$ bundle binstubs cucumber

Install selenium-webdriver

To be able to run Cucumber scenarios which use Javascript you need selenium-webdriver.

Add it to the test group of your Gemfile:

group :test do
  gem 'cucumber-rails', require: false
  gem 'database_cleaner'
  gem 'factory_girl_rails'
  gem 'selenium-webdriver'
end

And install it:

$ bundle install

Make sure Cucumber is working correctly

To do that, let’s develop a simple feature.

# features/home_page.feature
Feature: Home page

  Scenario: Viewing application's home page
    Given there's a post titled "My first" with "Hello, BDD world!" content
    When I am on the homepage
    Then I should see the "My first" post
# features/step_definitions/home_page_steps.rb
Given(/^there's a post titled "(.*?)" with "(.*?)" content$/) do |title, content|
  @post = FactoryGirl.create(:post, title: title, content: content)
end

When(/^I am on the homepage$/) do
  visit root_path
end

Then(/^I should see the "(.*?)" post$/) do |title|
  @post = Post.find_by_title(title)

  page.should have_content(@post.title)
  page.should have_content(@post.content)
end
# config/routes.rb
Myapp::Application.routes.draw do

  root to: "posts#index"

end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController

  def index
    @posts = Post.all
  end

end
<!-- app/views/posts/index.html.erb -->
<ul>
  <% @posts.each do |post| %>
    <li>
      <%= post.title %><br />
      <%= post.content %>
    </li>
  <% end %>
<ul>

Now run the feature file and you should see it pass:

$ bin/cucumber features/home_page.feature

Congratulations for making it this far. You should now be fully equipped to work in the BDD cycle and deliver clean, working code.