Karan
Karan

Reputation: 15104

Devise Test Helper - sign_in does not work

For some reason, I can not get the devise helper method sign_in to work. current_user keeps on being null. Any idea what the problem could be?

Test:

  before :each do
    @user = FactoryGirl.create :user
    sign_in @user
  end

  describe "GET index" do
    it "assigns all subscribers as @subscribers" do
      subscriber = @user.subscribers.create! valid_attributes
      get :index
      assigns(:subscribers).should eq([subscriber])
    end
  end

Implementation:

  def index
    @subscribers = current_user.subscribers.all    <------- ERROR

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @subscribers }
    end
  end

Error:
 NoMethodError:
       undefined method `subscribers' for nil:NilClass

Any help is appreciated. Thanks!

Upvotes: 14

Views: 12676

Answers (4)

ronalchn
ronalchn

Reputation: 12335

For versions of Devise 4.2.0+, the Devise::TestHelpers have been deprecated. Instead, Devise::Test::ControllerHelpers should be used.

RSpec.configure do |config|
  config.include Devise::Test::ControllerHelpers, type: :controller
end

changelog


For the spec, make sure to include Devise::TestHelpers. To make it easy, in my spec/spec_helper.rb, I have:

RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller
end

which automatically includes it for all controller specs.

Also, you need to do this to get sign_in to work:

@request.env["devise.mapping"] = Devise.mappings[:user]
get :new

It is probably best to add @request.env["devise.mapping"] = Devise.mappings[:user] to your before(:each). (Note you can do this in your config if you don't want to do this for every controller).


For the current_user part, make sure you have a model User, where you call devise

class User < ActiveRecord::Base
  # call devise to define user_signed_in? and current_user
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable
  # though you don't have to include all these modules
end

Devise uses the call in the User model to define user_signed_in? and current_user in your controllers. The reason is that if you have:

class Admin < ActiveRecord::Base
  devise
end

then Devise will have methods admin_signed_in? and current_admin defined.

Upvotes: 6

Donovan Thomson
Donovan Thomson

Reputation: 2525

I encountered this problem when trying to test that an SSO endpoint I was writing was creating an session for a user. Since it only applied to one test, I just needed to add the following block before my test

  before do
    @request.env["devise.mapping"] = Devise.mappings[:user]
    user = FactoryGirl.create(:user, :email => email, :password => "password")
    user.confirm!
  end

  it "should create and session for the user and redirect to home page" do

Upvotes: 0

Jellicle
Jellicle

Reputation: 30256

If you include the Confirmable module in your User model (or other devise-authenticatable model), then the test @user you create must be confirmed for the sign_in to take effect:

before :each do
  @user = FactoryGirl.create :user
  @user.confirm!
  sign_in @user
end

(I see that this wasn't your issue, but perhaps another reader shall benefit from it.)

Upvotes: 23

Tyler Gannon
Tyler Gannon

Reputation: 1012

Looks like you solved this, judging by your code. I have had this happen before, and for some reason it gets me every time.

The rspec/rails scaffold for controller specs won't work with Devise::TestHelpers out of the box.

get :index, {}, valid_session

The valid_session call overwrites the session stuff that Devise sets up. Remove it:

get :index, {}

This should work!

Upvotes: 12

Related Questions