Rails: How To Test Omniauth In Your Sessions Controller

In my current project, I’ve implemented a Sign in with Github button using the Omniauth Github gem, which creates or finds the user and creates a user session. My sessions controller is currently very basic:

# sessions_controller.rb
class SessionsController < ApplicationController

  def create
    auth = request.env["omniauth.auth"]
    user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
    session[:user_id] = user.id
    redirect_to root_url, :notice => "Signed in!"
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_url, :notice => "Signed out!"
  end

end

To see more details from my code, check out my Omniauth Github tutorial.

However, testing the sessions controller gets complicated, since you need to mock omniauth and add the “omniauth.auth” hash to the test request environment.

After doing some digging around, I found that Omniauth has a wiki page dedicated specifically to testing.

In your spec helper, just add the following code (with your own omniauth hash, which only has information you’re saving in your user model) :

  OmniAuth.config.test_mode = true
  omniauth_hash = { 'provider' => 'github',
                    'uid' => '12345',
                    'info' => {
                        'name' => 'natasha',
                        'email' => 'hi@natashatherobot.com',
                        'nickname' => 'NatashaTheRobot'
                    },
                    'extra' => {'raw_info' =>
                                    { 'location' => 'San Francisco',
                                      'gravatar_id' => '123456789'
                                    }
                    }
  }

  OmniAuth.config.add_mock(:github, omniauth_hash)

So your spec helper should look something like this:

# spec_helper.rb
require 'rubygems'

# This file is copied to spec/ when you run 'rails generate rspec:install'
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  OmniAuth.config.test_mode = true
  omniauth_hash = { 'provider' => 'github',
                    'uid' => '12345',
                    'info' => {
                        'name' => 'natasha',
                        'email' => 'hi@natashatherobot.com',
                        'nickname' => 'NatashaTheRobot'
                    },
                    'extra' => {'raw_info' =>
                                    { 'location' => 'San Francisco',
                                      'gravatar_id' => '123456789'
                                    }
                    }
  }

  OmniAuth.config.add_mock(:github, omniauth_hash)

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
  ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

  RSpec.configure do |config|

    # Add Factory Girl
    config.include FactoryGirl::Syntax::Methods

    # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
    config.fixture_path = "#{::Rails.root}/spec/fixtures"

    # If you're not using ActiveRecord, or you'd prefer not to run each of your
    # examples within a transaction, remove the following line or assign false
    # instead of true.
    config.use_transactional_fixtures = true

    # If true, the base class of anonymous controllers will be inferred
    # automatically. This will be the default behavior in future versions of
    # rspec-rails.
    config.infer_base_class_for_anonymous_controllers = false

    # Run specs in random order to surface order dependencies. If you find an
    # order dependency and want to debug it, you can fix the order by providing
    # the seed, which is printed after each run.
    #     --seed 1234
    config.order = "random"

end

Finally, whenever you need the Omniauth response hash, you can just use OmniAuth.config.mock_auth[:github] – I used this both in my sessions controller and in my user model specs. If you have the rspec-rails gem installed, your session controller spec file will look something like this:

# sessions_controller_spec.rb
require 'spec_helper'

describe SessionsController do

  before do
    request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:github]
  end

  describe "#create" do

    it "should successfully create a user" do
      expect {
        post :create, provider: :github
      }.to change{ User.count }.by(1)
    end

    it "should successfully create a session" do
      session[:user_id].should be_nil
      post :create, provider: :github
      session[:user_id].should_not be_nil
    end

    it "should redirect the user to the root url" do
      post :create, provider: :github
      response.should redirect_to root_url
    end

  end

  describe "#destroy" do
    before do
      post :create, provider: :github
    end

    it "should clear the session" do
      session[:user_id].should_not be_nil
      delete :destroy
      session[:user_id].should be_nil
    end

    it "should redirect to the home page" do
      delete :destroy
      response.should redirect_to root_url
    end
  end

end

Happy testing!

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • Ricardo Enrique Berdejö Mora

    Thank you so much. I’m pretty new in Rails and this has been killing me.

  • Geoffroy

    Thanks a lot !! very helpful !

  • amxn

    Hey Natasha, I guess your Templating engine has broken. Only HTML entities instead of their symbol counterparts are displayed. ( Eg: < instead of < )

  • Adrian Stoica

    Thank you . This really helped me a lot .