RSpec Rails
Guide Tasks
  • Read Tutorial

What rspec-rails Provides

RSpec has been one of the more popular testing frameworks through the years for Ruby application. RSpec Rails is a very popular extension of RSpec that is specifically engineered to work with the Rails framework. It's completely possible to use the standard RSpec gem in a Rails project as well, however I like some of the custom features that the rspec-rails gem offers.

With the gem you can:

  • Run automated model, view, controller, and feature specs

  • Integrate various testing libraries, such as FactoryGirl and Capybara

  • Use outside testing services to ensure that your applications are passing all of their tests prior to deployment (my personal favorite is CodeShip)

If you're a professional Rails developer (or you aspire to be one), knowing how to utilize testing libraries is vital, and RSpec is one of the foundational testing libraries used throughout the Rails community, so it's a good tool to have in your coding arsenal.

Drawbacks

I personally haven't run into any major drawbacks to using rspec-rails. However there are developers who feel that it's not necessary and prefer to simply use the test/unit testing library that ships with all Rails installs by default.

I've used both libraries in a number of applications and I personally prefer the syntax that RSpec provides, however I recommend you working with both libraries since many legacy applications were created with test/unit. In fact, one of my largest clients, Eventbrite, uses test/unit for their test suite and it works quite well.

Implementation / RSpec Rails Example

To make things simple, for this walk through I ran the rails app generator with the flag -T so that it would skip the installation of test/unit, so the command was:

rails new rspec-rails-tutorial -T

After creating and migrating the database we have everything we need for installing rspec-rails. First add the following line to the Gemfile:

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

After running bundle install we can run the generator:

rails generate rspec:install

This will generate a series of files, including:

  • .rspec - this is where you can place some presets, such as the color to be displayed in the terminal

  • spec/ - this spec directory is where all of the tests and configuration options are stored

  • spec/spec_helper.rb - the spec helper file has the settings directly related to the RSpec test setup

  • spec/rails_helper.rb - similar to the spec_helper, the rails_helper is where you can place additional configuration options, typically these configuration options relate directly to the Rails application whereas the spec_helper file relates to the tests themselves.

Opening up the .rspec file you can see it's pre-loaded with two options:

--color
--require spec_helper

We won't need to change anything in this file for this walk through, however I wanted to show you what that file entailed since more complex applications could benefit from customizing this option file.

One of the nice things about the rspec-rails gem is that it will automatically generate sample spec files, let's see this in action. In the terminal run a controller generator:

rails g controller Pages home about contact faqs

In addition to the normal files that were generated, if you look at the terminal you can see that the application created sample RSpec files:

invoke  rspec
create    spec/controllers/pages_controller_spec.rb
create    spec/views/pages
create    spec/views/pages/home.html.erb_spec.rb
create    spec/views/pages/about.html.erb_spec.rb
create    spec/views/pages/contact.html.erb_spec.rb
create    spec/views/pages/faqs.html.erb_spec.rb

Opening up the pages controller spec we'll see the sample spec:

require 'rails_helper'

RSpec.describe PagesController, type: :controller do

  describe "GET #home" do
    it "returns http success" do
      get :home
      expect(response).to have_http_status(:success)
    end
  end

  describe "GET #about" do
    it "returns http success" do
      get :about
      expect(response).to have_http_status(:success)
    end
  end

  describe "GET #contact" do
    it "returns http success" do
      get :contact
      expect(response).to have_http_status(:success)
    end
  end

  describe "GET #faqs" do
    it "returns http success" do
      get :faqs
      expect(response).to have_http_status(:success)
    end
  end

end

Pretty cool, right? The RSpec suite took in the page parameters and assumed that we would want tests to ensure that the pages can all be accessed with a success message. Let's run the controller spec, run the following command in the terminal:

rspec spec/controllers/

This will output:

....

Finished in 0.03083 seconds (files took 3.98 seconds to load)
4 examples, 0 failures

Nice, all of the tests are passing (green is always good). This ran each of the controller specs and bypassed all of the other specs that were generated. This is very helpful if you're trying to debug your controllers without having to waste time by having the full test suite run each time. In fact we can drill it down more, imagine that you have a huge application with 100 controllers, if you want to only run the tests for a single controller you can run:

rspec spec/controllers/pages_controller_spec.rb

This will give us the same output as above, but in this case it drilled down and only looked through that specific file. Now what happens if you only wanted to run a single spec? That's easy too! Trying running (assuming you have a spec starting on line 26 of the pages_controller_spec.rb file):

rspec spec/controllers/pages_controller_spec.rb:26

Now this outputs:

.

Finished in 0.01904 seconds (files took 4.03 seconds to load)
1 example, 0 failures

So it's possible to drill down to a single spec, this is incredibly helpful when debugging and will make your TDD (Test Driven Development) process more efficient.

If you open up the view specs you will see specs that relate directly to testing your app views. Personally I'm not a huge fan of testing views like how the generator recommends, I prefer integrating integration tests with libraries such as Capybara, but feel free to play around with the views.

Let's see what we get when we create a model:

rails g model Article title:string author:string

This creates a spec file spec/models/article_spec.rb for our new Article model. After running rake db:migrate we can run:

rspec spec/models

Out of the box RSpec doesn't create any live specs, it simply sets up the file so we can add our tests, so you'll see the output:

*

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Article add some examples to (or delete) /Users/admin/code/devcamp-tutorials/gem-reviews/rspec-rails-tutorial/spec/models/article_spec.rb
     # Not yet implemented
     # ./spec/models/article_spec.rb:4


Finished in 0.00058 seconds (files took 3.99 seconds to load)
1 example, 0 failures, 1 pending

Even though it's not following the Red/Green/Refactor process of TDD, let's create a test that we know should pass right away. Open up the model spec and update it as below:

# spec/models/article_spec.rb

require 'rails_helper'

RSpec.describe Article, type: :model do
  describe "creation" do
    it "can be created if valid" do
      article = Article.create(title: "My Article", author: "Jon Snow")
      expect(article).to be_valid
    end
  end
end

Running the model specs again and you'll see that our tests are passing! Let's create a failing test and then implement the code to get it green/passing. Add another spec to the model file inside of the creation describe block:

# spec/models/article_spec.rb

it "will not be created if not valid" do
  article = Article.create(title: "My Article")
  expect(article).to_not be_valid
end

Running the model specs now will give us the following failure message:

.F

Failures:

  1) Article creation will not be created if not valid
     Failure/Error: expect(article).to_not be_valid
       expected #<Article id: 1, title: "My Article", author: nil, created_at: "2016-01-19 18:49:45", updated_at: "2016-01-19 18:49:45"> not to be valid
     # ./spec/models/article_spec.rb:12:in `block (3 levels) in <top (required)>'

Finished in 0.16136 seconds (files took 2.14 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/models/article_spec.rb:10 # Article creation will not be created if not valid

In this case it's actually good that this failed, so let's implement the code to fix it, we need to add a validation to ensure an acticle cannot be created unless it has a title and author attribute, so let's update the model file:

# app/models/article.rb

class Article < ActiveRecord::Base
  validates_presence_of :title, :author
end

Running the model tests again and you'll see that the model test is now passing, that's a great feeling, right?!

This walkthrough isn't meant to teach you TDD, it's simply showing how to use the rspec-rails gem, so if you're wanting more information I'd recommend Rails 4 Test Prescriptions, it's one of my favorite books walking through how to practically build a Rails test suite.

One last thing I'll leave you with, RSpec has a great feature that will actually print out your tests in a way that they could be read in plain english. Run the following command:

rspec spec/models --format documentation

This will output the specs like below:

Article
  creation
    can be created if valid
    will not be created if not valid

Finished in 0.01875 seconds (files took 2.13 seconds to load)
2 examples, 0 failures

I love this feature because it means that if you build a comprehensive test suite with properly named specs you can essentially print out all of the features for your application and give them to non-technical managers, investors, etc.

Code

Resources