No More Seat Costs: Semaphore Plans Just Got Better!

    23 Mar 2020 · Software Engineering

    Setting up the BDD stack on a new Rails 4 application

    6 min read
    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.

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar
    Writen by: