- 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 terminalspec/
- this spec directory is where all of the tests and configuration options are storedspec/spec_helper.rb
- the spec helper file has the settings directly related to the RSpec test setupspec/rails_helper.rb
- similar to thespec_helper
, therails_helper
is where you can place additional configuration options, typically these configuration options relate directly to the Rails application whereas thespec_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.