Learn the First Best Practices for Rails and RSpec

Share this article

This article was peer reviewed by Thom Parkin. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Writing code without testing can be a deceptively smooth ride; one that, unfortunately, comes to a screeching halt when you start working with other developers. This tutorial is aimed at helping Ruby on Rails developers (especially the new folks) in setting up an RSpec test suite using best practices, in the Behaviour Driven Development way. I know that testing can be pain when you are just getting into it. There are lots of resources online to help you on this journey. I am hopeful this will be of benefit to new Ruby on Rails developers since I just started getting into testing myself.

In this tutorial we will learn the following: * How to setup – RSpec, Capybara, Shoulda-Matchers, Database Cleaner * How to create a factory using Factory Girl Rails and Faker * How to write Model Specs * How to write Controller Specs * How to write Feature Specs * How to check spec coverage using SimpleCov

Rails Application

rails new myapp

Installing RSpec

Open up your Gemfile and add the rspec-rails gem to the development and test group:

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

Install the gems:

bundle install

Now run:

rails generate rspec:install

This adds the spec directory and some skeleton files, including a .rspec file. Before going further, go ahead and remove the test directory that was generated with our Rails application.

Shoulda-Matchers and Database Cleaner

Open up your Gemfile and add a test group containing the shoulda-matcher and database_cleaner gems:

Gemfile

group :test do
  gem 'shoulda-matchers', '~> 3.0', require: false
  gem 'database_cleaner', '~> 1.5'
end

Run bundle install.

Shoulda-Matchers Configuration

Shoulda-Matchers provides one-line matchers to RSpec used in testing Rails functionality which we will see briefly. Open your spec/rails_helper.rb file and configure shoulda-matchers to work with RSpec by pasting in the following:

require 'shoulda/matchers'

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

Database Cleaner Configuration

Database Cleaner is a set of strategies for cleaning your database in Ruby between test runs. It helps to ensure a clean state for testing.

To integrate database_cleaner, make the following adjustment to spec/rails_helper.rb:

config.use_transactional_fixtures = false

Create a new directory called support inside of your spec directory:

mkdir spec/support

Inside it, create a new file, database_cleaner.rb and paste in the following:

RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end

With that, database_cleaner is set up and will clean the database between each unit test and test suite.

Capybara Setup

Capybara is an automation framework used for creating functional tests that simulates how users will interact with your application. Add the capybara gem to the :development, :test group in your Gemfile, the same group where you added rspec-rails:

gem 'capybara', '~> 2.5'

and bundle install.

Open up your spec_helper.rb and require the capybara gem:

***spec/spec_helper.rb***
...
require 'capybara/rspec'

Faker and Factory Girl Setup

Faker is useful in generating random data for your test. You will see how to use it with factory_girl_rails in making factory. Add the faker gem to the :test group in your Gemfile:

gem 'faker', '~> 1.6.1'

Factory Girl allows you create objects that you need in your tests which can include default values. With faker, you will be able to create random objects for your test instead of using just one default value.

Add factory_girl_rails gem to :development, :test group:

gem 'factory_girl_rails', '~> 4.5.0'

Then bundle install.

At this point, your Gemfile should look like this:

source 'https://rubygems.org'


gem 'rails', '4.2.4'
gem 'sqlite3'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0', group: :doc

group :development, :test do
  gem 'byebug'
  gem 'rspec-rails', '~> 3.4'
  gem 'factory_girl_rails', '~> 4.5'
  gem 'capybara', '~> 2.5'
end

group :development do
  gem 'web-console', '~> 2.0'
  gem 'spring'
end

group :test do
  gem 'shoulda-matchers', '~> 3.0', require: false
  gem 'database_cleaner', '~> 1.5'
  gem 'faker', '~> 1.6.1'
end

Creating a Factory

Create a directory named factories in your spec folder, also adding a file named after your model. E.g contacts.rb

***spec/factories/contacts.rb***

FactoryGirl.define do
  factory :contact do
    full_name     { Faker::Name.name }
    email         { Faker::Internet.email }
    phone_number  { Faker::PhoneNumber.phone_number }
    address       { Faker::Address.street_address }
  end
end

In the above, I just showed you how to create fixtures using factory_girl_rails and faker. You do not have to explicitly enter objects. Factory Girl uses the random (fake) values generated by faker to create factories that will be used whenever you run your test.

Model Specs

We want to ensure that the factory we created above is valid for the model. Create a file in your model/spec folder for your model:

***spec/models/contact_spec.rb***

require 'rails_helper'

RSpec.describe Contact, type: :model do
  it "has a valid factory" do
    expect(contact).to be_valid
  end
end

From your terminal run:

rspec spec/models/contact_spec.rb

This will show an error, because you do not have Contact model. Go to the terminal, create the model, and migrate your database:

rails g model Contact full_name:string email:string phone_number:integer address:text
rake db:migrate

Run the spec again and viola! It should pass :)

We’ll wrap up the model test using shoulda-matchers to validate the presence of full name, email, phone number and address. In your contact model spec, create a new describe block:

***spec/models/contact_spec.rb***

  describe Contact do
    it { is_expected.to validate_presence_of(:full_name) }
    it { is_expected.to validate_presence_of(:email) }
    it { is_expected.to validate_presence_of(:phone_number) }
    it { is_expected.to validate_presence_of(:address) }
  end

Run your spec and watch it fail. To have it pass, go to the model and implement the validation code:

***app/model/contact.rb***

class Contact < ActiveRecord::Base
  validates_presence_of :full_name, :email, :phone_number, :address
end

Run the spec again and it will pass. Before you move to controller spec, I want to explain some of the terms we have used above.

RSpec DSL Pieces

RSpec is a DSL for creating executable examples of how code is expected to behave, organized in groups.

describe creates an example group. It takes an argument that tells what the spec is about. The argument can be a class, module, method or a string description.

it creates an example and takes a description of the example. (Example; it has status code 400). It is a best practice to limit the spec description to 40 characters or less. If it needs to be longer, you probably should consider using a context to create “sub-contexts” of a describe block.

expect lets you express expected outcomes on an object in an example. It takes an object or block and is used with either to or not_to alongside a matcher (e.g eq(), be_valid). While there are many built-in matchers, gems like Shoulda Matchers add even more to make your specs expressive and concise.

Controller Spec

The first controller spec will test if a new contact gets created with valid attributes. Open up your terminal and generate the controller:

rails g controller Contacts

This will automatically generate the necessary folders for your controller spec. You can see them at spec/controllers.

It’s time to write the spec to test the create action:

***spec/controllers/contacts_controller_spec.rb***

require 'rails_helper'

RSpec.describe ContactsController, type: :controller do

  describe "POST #create" do
    context "with valid attributes" do
      it "create new contact" do
        post :create, contact: attributes_for(:contact)
        expect(Contact.count).to eq(1)
      end
    end
  end
end

Run the spec:

rspec spec/controllers/contacts_controller_spec.rb

This will fail because you do not have a route that matches your controller. Open up the routes file and drop this in:

***config/routes.rb***
...
resources :contacts

Run your spec again and it should still fail with the error The action 'create' could not be found for ContactsController.

Open up your controller and drop in the necessary code to create a new contact.

***app/controllers/contacts_controller.rb***

class ContactsController < ApplicationController
  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(contact_params)

    respond_to do |format|
      if @contact.save
        format.html { redirect_to @contact }
        format.json { render :show, status: :created, location: @contact }
      else
        format.html { render :new }
        format.json { render json: @contact.errors, status: :unprocessable_entity}
      end
    end
  end

  private

  def contact_params
    params.require(:contact).permit(:full_name, :email, :phone_number, :address)
  end
end

Save and run the spec one more time, and it should pass.

Following the spec above, write a spec that uses invalid attributes to create a new contact. This spec should check that the contact is not created:

***spec/controllers/contacts_controller_spec.rb***

context "with invalid attributes" do
  it "does not create a new contact" do
    post :create, contact: attributes_for(:invalid_contact)
      expect(Contact.count).to eq(0)
    end
  end
end

Using the spec you wrote above as an example, you can churn out specs for other controller actions.

Feature Spec

Feature Specs are high-level tests that work through your application ensuring that every component works. They are usually written from the perspective of a user.

For the purpose of this tutorial, you will write a spec to test the creation of a new contact. Using the capybara gem, the specs will fill in a form with valid attributes and test that the show page displays the expected text when a contact is created.

Open your terminal or text editor and create folders and a file following the path below:

spec/features/contacts/create_spec.rb

Paste the following code into that file:

***spec/features/contacts/create_spec.rb***
require 'rails_helper'

RSpec.feature "Contact", :type => :feature do
  scenario "Create a new contact" do
    visit "/contacts/new"

    fill_in "Full name", :with => "My Name"
    fill_in "Email", :with => "my@email.com"
    fill_in "Phone number", :with => "123456789"
    fill_in "Address", :with => "34, Allen Way, OA"

    click_button "Create Contact"

    expect(page).to have_text("My Name")
  end
end

The spec above tests if the show page has the text My Name which is the value written into the full name input. Run this spec and it should fail.

First add a show action to your controller:

***app/controllers/contacts_controller.rb

def show
  @contact = Contact.find(params[:id])
end

Create the following files in your contacts view: new.html.erb, _form.html.erb, show.html.erb and fill in with the following code:

***app/views/contacts/_form.html.erb***

<%= form_for(@contact) do |f| %>
  <% if @contact.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@contact.errors.count, "error") %> prohibited this contact from being saved:</h2>

      <ul>
      <% @contact.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :full_name %><br>
    <%= f.text_field :full_name %>
  </div>
  <div class="field">
    <%= f.label :email %><br>
    <%= f.text_area :email %>
  </div>
  <div class="field">
    <%= f.label :phone_number %><br>
    <%= f.text_area :phone_number %>
  </div>
  <div class="field">
    <%= f.label :address %><br>
    <%= f.text_area :address %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

***app/views/contacts/new.html.erb***

<h2>Create new contact</h2>

<%= render 'form' %>

***app/views/contacts/show.html.erb***

<p id="notice"><%= notice %></p>

<p>
  <strong>Full Name:</strong>
  <%= @contact.full_name %>
</p>

<p>
  <strong>Email:</strong>
  <%= @contact.email %>
</p>

<p>
  <strong>Phone Number:</strong>
  <%= @contact.phone_number %>
</p>

<p>
  <strong>Address:</strong>
  <%= @contact.address %>
</p>

Run the spec and it should pass this time.

Coverage using SimpleCov

The SimpleCov gem is a code coverage analysis tool for Ruby. You will use it in your application to see how much of your code is tested. To install, open up the Gemfile and add to the test group:

gem 'simplecov', :require => false

Run bundle install.

Next open your spec helper are require simplecov

require 'simplecov'
SimpleCov.start

The next time you run the specs, a new folder with the name coverage will be generated. Open up your browser and point to: your-app-directory/coverage/index.html to see the coverage statistics for your application. You’ll want to add the coverage folder to .gitignore file so it does not get added to your remote repository.

Conclusion

This article aims to give you a foundation for setting up RSpec for your Rails application. These are the basic steps and, from here, you’ll be able to explore the vast syntax of RSpec and start refining your specs. At this point, you should be able to:

  • Set up a test suite for your application.
  • Write model, controller, and feature specs.

Here are some more resources to help you:

With more practice, in no time you will be fluent in testing your Rails application.

Frequently Asked Questions about Rails and RSpec Best Practices

What are the key benefits of using RSpec in Rails?

RSpec is a testing tool for Ruby, commonly used with Rails. It is designed to make Test-Driven Development (TDD) a productive and enjoyable experience. The key benefits of using RSpec in Rails include its readability, flexibility, and modularity. RSpec uses a simple, English-like syntax that makes it easy to read and write tests. It is also highly flexible, allowing you to write tests in a way that suits your application and testing style. Furthermore, RSpec is modular, meaning you can choose to use its various components independently or together, depending on your needs.

How do I install RSpec in my Rails application?

To install RSpec in your Rails application, you need to add it to your Gemfile. Open your Gemfile and add the following line: gem 'rspec-rails', '~> 3.8'. Then, run bundle install to install the gem. After that, you need to set up RSpec in your application by running rails generate rspec:install. This will create a spec directory and some files that are used for configuration.

What are some best practices for writing RSpec tests in Rails?

Some best practices for writing RSpec tests in Rails include keeping your tests DRY (Don’t Repeat Yourself), using factories instead of fixtures, and testing behavior, not implementation. You should also strive to write clear, readable tests and to keep your test suite organized.

How can I run my RSpec tests?

To run your RSpec tests, you can use the rspec command followed by the path to the spec file. For example, rspec spec/models/user_spec.rb would run the tests in the user_spec.rb file. If you want to run all your tests, you can simply use the rspec command without specifying a file.

How can I use RSpec with Capybara for feature testing?

Capybara is a tool that integrates with RSpec to allow for feature testing, which simulates how a user would interact with your application. To use RSpec with Capybara, you need to add Capybara to your Gemfile and then require it in your spec_helper.rb or rails_helper.rb file. You can then use Capybara’s DSL in your feature specs.

What is the difference between let and before in RSpec?

In RSpec, let and before are both used to set up state for your tests, but they work in slightly different ways. let is lazy-evaluated, meaning it is not evaluated until it is called. This can improve performance for expensive setup. before, on the other hand, is evaluated before each example in the group.

How can I test private methods with RSpec?

In general, you should not test private methods directly. Instead, you should test the public methods that use them. However, if you really need to test a private method, you can do so by sending the method to the object.

How can I use mocks and stubs in RSpec?

Mocks and stubs are used in RSpec to isolate the object under test from its dependencies. A stub replaces a method with code that returns a specified result, while a mock also asserts that the method is called a certain number of times.

What is the purpose of the describe and it blocks in RSpec?

The describe and it blocks in RSpec are used to organize your tests and provide context. The describe block is used to group related examples, while the it block is used to specify a single example.

How can I share common setup code across multiple tests in RSpec?

In RSpec, you can share common setup code across multiple tests using before, after, and around hooks. These hooks allow you to specify code that should be run before, after, or around each example or group of examples.

Kingsley SilasKingsley Silas
View Author

Kingsley Silas is a web developer from Nigeria. He has a hunger for acquiring new knowledge in every aspect he finds interesting.

GlennGRSpecRuby on Rails
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week