Setting up Rails testing with rspec, devise, and the gang

so much fun

Published January 23, 2015 #rails #happy_seed #testing #ruby

This article was published a while ago and may contain obsolete information!

The goal is to get features out fast, and iterate on them quickly. Does anyone care about it? What do they care about? How do we make it better?

As projects get bigger, both in terms of people using the site as well as people working on the site, testing and quality become relatively more important. Adding tests introduces drag, and the theory is that you invest now for payoffs later. What’s the least amount of drag we can add to the process to get us in a good place for when it will start to pay off?

RSpec feature specs lets developers create testing click paths through the application, looking at the flow though the app. It doesn’t focus enough on the design details, and going through a test driving development process with this way will end up with sites ‘working’ using only an engineer’s defination, so as a MVP implementor you’ll need to find a way to resist the temptation, no matter how nifty the backend flow state is.

This article will go through how the testing environment is set up on HappySeed where we focus mainly on RSpec feature specs. Seed does this all automatically, but its nice to have it written down in a one place so we all know what’s going on. Let’s get into it.

Install RSpec, FactoryGirl, Capybara, and Guard

Lets fire up a new rails app, and pass the -T option to not include TestUnit.

$ rails new test_app -T

Now add some gems to the Gemfile.

group :development, :test do
  gem 'rspec-rails'
  gem 'factory_girl_rails'
  gem 'capybara'
  gem 'guard-rspec'
  gem 'spring-commands-rspec'
  gem 'vcr'

group :test do
  gem 'webmock'

Lets go through this:

If you are interested in using cucumber, see below for steps to integrate it.

Base configuration of rspec and guard

Lets make sure that spring knows about rspec:

$ bundle install
$ spring binstub --all
* bin/rake: spring already present
* bin/rspec: generated with spring
* bin/rails: spring already present

This creates a bin/rspec command that lets us run our tests. Lets create a Guardfile and then tell guard to use this command:

$ guard init

Then edit the newly generated Guardfile to use the spring version of the rspec runner. I also like to run all of the tests when I start up guard, because the more the merrier. In Guardfile, change the guard :rspec line to:

guard :rspec, cmd: "bin/rspec", all_on_start: true do

Now we install the rspec files:

$ rails g rspec:install

This creates 3 files:

create  .rspec
create  spec
create  spec/spec_helper.rb
create  spec/rails_helper.rb

I like to have the output in documentation format by default, so we can edit the .rspec file to be:

--require spec_helper
--format documentation

Add login support and factory girl to spec_helper

Getting feature and controller specs to work with authentication is a bit tricky. We’re going to assume that you use devise for authentication, but this should support anything that uses warden:

Add devise to Gemfile:

gem 'devise'

Create spec/support/controller_helpers.rb

module ControllerHelpers
  def login_with(user = double('user'), scope = :user)
    current_user = "current_#{scope}".to_sym
    if user.nil?
      allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => scope})
      allow(controller).to receive(current_user).and_return(nil)
      allow(request.env['warden']).to receive(:authenticate!).and_return(user)
      allow(controller).to receive(current_user).and_return(user)

At the top of spec_helper.rb add:

require_relative 'support/controller_helpers'
require 'devise'

and in the config block add:

  config.include ControllerHelpers, type: :controller

  config.after do

Then edit rails_helper.rb and put the following in the config block:

  config.include FactoryGirl::Syntax::Methods
  config.include Devise::TestHelpers, type: :controller
  config.include Warden::Test::Helpers

If you are getting uncaught throw :warden errors then make sure you have the right includes in the right config files!

This gives you a few things:

Example controller spec

This example assumes that you’ve already setup devise. If not, you can get something up and running doing:

$ rails g devise:install && rails g devise User && rake db:migrate

(I go into slightly more detail in setting up devise for authentication.)

Assuming that you’ve already setup devise, lets look at how to implement a controller spec. First lets create a simple scaffold for a post:

$ rails g scaffold post name:string

And lets make it require authentication in posts_controller.rb:

class PostsController < ApplicationController
  before_action :authenticate_user!

And replace posts_controller_spec.rb with:

require 'rails_helper'

RSpec.describe PostsController, :type => :controller do
  describe "anonymous user" do
    before :each do
      # This simulates an anonymous user
      login_with nil

    it "should be redirected to signin" do
      get :index
      expect( response ).to redirect_to( new_user_session_path )

When we run guard, a few of the scaffold generate things will fail, since we are now forcing signin to check out this page. Our spec will correctly redirect the user to sign in.

Lets now simulate a logged in user:

  it "should let a user see all the posts" do
    login_with create( :user )
    get :index
    expect( response ).to render_template( :index )

Guard should try and run this tests but fail because we can’t configured a user factory.

Lets set that up now in spec/factories/user.rb:

FactoryGirl.define do
  sequence :email do |n|

FactoryGirl.define do
  factory :user, :class => 'User' do
    password '12345678'
    password_confirmation '12345678'

Going back to the guard window, press return to run everything again and verify. (Guard doens’t know that the posts_controller_spec.rb depends upon the user factory changing.)

Example rspec feature

Feature specs are more interesting, since they let you walk through the site and how it works. Lets build one now:

$ rails g rspec:feature add_new_post
      create  spec/features/add_new_posts_spec.rb

And lets add something there now:

require 'rails_helper'

feature "AddNewPosts", :type => :feature do
  it "should require the user log in before adding a post" do
    password = "123456789"
    u = create( :user, password: password, password_confirmation: password )

    visit new_post_path

    within "#new_user" do
      fill_in "user_email", with:
      fill_in "user_password", with: password

    click_button "Log in"

    within "#new_post" do
      fill_in "post_name", with: "Post title"

    click_link_or_button "Create Post"

    expect( Post.count ).to eq(1)
    expect( eq( "Post title")

This first creates a new user in the database, and then

  1. Visits the new_post_path url.
  2. We should be shown the login form, and we fill it out
  3. Click the log in button
  4. Fill out the new post form
  5. Create the post
  6. Make sure that it’s in the database

If you want to skip the steps of manually logging in the user, you can use the login_as method like so:

  it "should create a new post with a logged in user" do
    login_as create( :user ), scope: :user

    visit new_post_path
    # puts page.body

    within "#new_post" do
      fill_in "post_name", with: "Post title"

    click_link_or_button "Create Post"

    expect( Post.count ).to eq(1)
    expect( eq( "Post title")


Setting up webmock and VCR

Webmock stops requests from hitting the network in the test environment. Lets turn this on by editing rails_helper.rb:

In rails_helper.rb, below require 'rails/rspec':

require 'webmock/rspec'

And VCR lets you record and play back tests in the test environment, so the first time it’s called it lets it get through, and then on subsequent requests it plays back a known response. Put this at the bottom of rails_helper.rb to configure:

VCR.configure do |c|
  c.cassette_library_dir  = Rails.root.join("spec", "vcr")
  c.hook_into :webmock

Example usage of webmock and VCR

Word comes down from up high that to facilitate decision making, a key bit of information is the current phase of the moon when something is posted to the site. We can pull the data from the Cerridwen site, but how do we set up tests for this? We certainly don’t want our test suite to fail because the phase of the moon changed!

This is where webmock and VCR comes in. Webmock by itself will throw an error when a request is made over the network. VCR will let us record and play back network request – using cassettes no less – so that we isolate changing API responses from our tests.

First lets write our code to look up the phase of the moon on the create post action. Let’s add a field

$ rails g migration add_phase_of_moon_to_posts moon_phase:string && rake db:migrate

Then add the lookup to our app/controllers/posts_controller.rb and replace the def create method:

  require 'json'
  require 'net/http'
  def create
    @post =

    moon_json = Net::HTTP.get( URI.parse("" ))
    moon = JSON.parse( moon_json ).first

    @post.moon_phase = "#{moon['phase']['trend']} in #{moon['position']['sign']}"

(This is not good style.)

Now lets run our test again.

Lots of unhandled http request errors! I’m going to ignore the scaffold generate tests and focus on our rspec feature. So, inside of spec/features/add_new_posts_spec.rb we can link the click action inside of a use_cassette block like so::

  it "should create a new post with a logged in user" do
    login_as create( :user ), scope: :user

    visit new_post_path
    # puts page.body

    within "#new_post" do
      fill_in "post_name", with: "Post title"

    VCR.use_cassette "waxing_in_pisces" do
      click_link_or_button "Create Post"

      expect( Post.count ).to eq(1)
      expect( eq( "Post title")
      expect( Post.first.moon_phase).to eq( "waxing in Pisces" )

And let the tests run. When tests are run, VCR is going to look for the file spec/vcr/waxing_in_pisces. If it’s not found, then it will make the request to the server. If the API requires authentication, for example an accesstoken when accessing a OAuth protected call, then you’ll need to configure you tests to have valid credentials for the first time. These credentials could be written into the spec/vcr directory, so make sure that you scrub that before things get checked in. (Or expire the access tokens.)

If it is found, then it plays it back and so the next time the suite is run it plays it back. Which is way faster.

Final notes

Testing has a place in software development, but it’s not actually clear where that is. Setting up test haresses and building out tests for every feature really slows things down in the beginning, and probably isn’t appropriate for one off, exploratory things. On the other hand, things tend to grow way past what anyone ever initially expected, and you always say “I wish there was a test suite” when inheriting code – or even more likely, looking at something you haven’t seen for a few months.

The rails community has a long history with Test Driven Development. The biggest advantage of this is that you write code that is testable, and code that is testable has all sorts of other qualities that is great, from clearly defined boundries of responsibility, good encapulation, and being able to look at small isolated pieces of the system and having it make sense. On the other hand, thebest code is the stuff you don’t need to maintain, and in some projects the weight of the test code is as large or larger than the code for the application features.

The biggest problem with testing is that it gets you in a wonderful, but wrong, flow state of development. In the begining of projects you should be focused on the user experience, and how the system is experienced from the outside. The highest level tests tend to be are user stories, which is below the level of brand and user experience.

Read next

See also

Setting up Devise with Twitter and Facebook and other Omniauth schemes without email addresses

Connect connect connect

Adding social login to your sites really makes it easier to get users onboard. Devise is great to help get an authentication system up and running, but there are a few tricky things to get right. The first challenge is that you don’t always get the user’s email address when the first connect. The second challenge is that we want to request the minimum permissions first so that the user is more likely to sign up, and gradually ask more as the time arises.

Read more

Dateslice: Writing rails extensions

adding date group_by to ActiveRecord

Ruby on Rails is a very modular framework since the merging with Merb in 2008. The opinionated conventions are implemented under using techniques that let you jump in and build your own components, picking and choosing different parts that let you build Rails apps in the same straightforward way you would if using the official modules. Let’s go through the dateslices gem which I wrote to extend active record so that we could better interact with the group by sql command when dealing with dates.

Read more

New HappySeed released

now with even more awesome

Make apps faster Head on over to the HappySeed website to get the latest version of HappyFunCorp’s starter application toolkit. HappySeed is a set of application templates to help you get started building out new sites. The main section is a rails application template plus a set of rails generators to help you get started with rails appliations quickly. These generators setup the configuration of the application in a standard way, and the full set of generators include many things for setting up a modern rails app and well as middleman apps.

Read more