23 Mar 2020 · Software Engineering · 6 min read

    Setting up the BDD stack on a new Rails 4 application

    Contents

    Note: An updated version of the tutorial is available at Semaphore
    Community
    .

    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.

    gherkin
    # 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
    
    erb
    
    


    <%% @posts.each do |post| %>


    • <%%= post.title %>
      <%%= post.content %>


    <%% end %>

    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.

    Want to discuss this article? Join our Discord.

    mm
    Writen by:
    Star us on GitHub