RobertJoseph
RobertJoseph

Reputation: 8158

How to write an Rspec controller test with authentication?

I have two models:

# app/models/user.rb
class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable

  has_many :teams, dependent: :destroy
end

and

# app/models/team.rb
class Team < ActiveRecord::Base
  belongs_to :user

  validates_presence_of :user
end

I am trying to test my TeamsController using Rspec and factory_girl_rails.

Before I can create a new Team I need an authenticated User.

I created a :user factory:

FactoryGirl.define do
  factory :user do
    first_name   "John"
    last_name    "Doe"
    email        {|n| "email#{n}@email.com" }
    mobile_phone "1235551234"
    company_name "Widgets Inc."
    password     "password"
  end
end

Here are the relevant parts of teams_controller_spec.rb:

require 'rails_helper'

RSpec.describe TeamsController, type: :controller do

  # This should return the minimal set of values that should be in the session
  # in order to pass any filters (e.g. authentication) defined in
  # TeamsController. Be sure to keep this updated too.
  let(:valid_session) { {} }

  describe "GET #index" do
    it "assigns all teams as @teams" do
      user = FactoryGirl.create(:user)

      team = Team.create!(name: "New Team Name", user: user)

      get :index, {}, valid_session
      expect(assigns(:teams)).to eq([team])
    end
  end
end

The test is failing:

1) TeamsController GET #index assigns all teams as @teams
     Failure/Error: get :index, {}, valid_session
     NoMethodError:
       undefined method `authenticate' for nil:NilClass

I don't understand how I need to populate :valid_session so that the test will pass. I thought I'd have to explicitly call an authenticate method but that might not be true. I'm trying to test the Team controller... not User authentication.

Any advice would be much appreciated.

Upvotes: 2

Views: 3174

Answers (1)

Yule
Yule

Reputation: 9764

I'd do this in your rails_helper:

module ControllerMacros    
  def sign_me_in
    before :each do
      @request.env['devise.mapping'] = Devise.mappings[:user]
      @current_user = FactoryGirl.create(:user)
      sign_in :user, @current_user
    end 
  end 
end

Rspec.configure do |config|
    #other rspec stuff
    config.include FactoryGirl::Syntax::Methods
    config.extend ControllerMacros, type: :controller
    config.include Devise::Test::ControllerHelpers, type: :controller
 end

Then in your controller spec, (provided you're requiring your rails_helper) you can just to sign_me_in whenever you want to be signed in and not bother about the valid_session:

RSpec.describe TeamsController, type: :controller do
  sign_me_in
  #etc...
end

However in your specific case you want to know who you're signed in as, so you can do this:

RSpec.describe TeamsController, type: :controller do
  describe "GET #index" do
    it "assigns all teams as @teams" do
      user = FactoryGirl.create(:user)
      team = Team.create!(name: "New Team Name", user: user)
      @request.env['devise.mapping'] = Devise.mappings[:user]
      sign_in :user, user
      get :index
      expect(assigns(:teams)).to eq([team])
    end
  end
end

The devise mappings line may not be required in your case, but can't say without inspecting your full app.

Upvotes: 2

Related Questions