Junchao Gu
Junchao Gu

Reputation: 1865

rspec + Devise: current_user is nil in tests

I am using Devise for my user logins and stuff and rspec for testing. I have looked at the Devise testing guide for rspec and mixined ControllerMicros to controller specs. And actually things are all working fine if I have tests organized like this:

  describe 'GET #index' do
    context 'user logged in but not admin' do
      login_user
      it 'should redirect to root_path for non_user' do
        get :index
        // I have asserted that the current_user here is not nil
        expect(response).to redirect_to(root_path)
      end
    end
  end

However, if I have 2 tests in the context and I got current_user is nil for the non-first test.

  describe 'GET #index' do
    context 'user logged in but not admin' do
      login_user
      it 'should redirect to root_path for non_user' do
        get :index
        // I have asserted that the current_user here is not nil
        expect(response).to redirect_to(root_path)
      end

      it 'should do some other thing' do
        get :index
        // the current_user method returns nil here
        expect(response).to redirect_to(root_path)
      end
    end
  end

And the worst part is that it seems this problem is not deterministic: happens somewhat randomly--cause after several failed runs the suite just passed on my computer(but still fails on Travis my build)

Some additional information:

the ControllerMacro.rb

module ControllerMacros
  def login_admin
    before(:each) do
      # @request.env["devise.mapping"] = Devise.mappings[:user]
      user = User.find_by(email: '[email protected]')
      user ||= FactoryGirl.create(:user, email: '[email protected]', uid: 'default_admin.controller.spec')
      admin = Admin.find_by(user_id: user.id)
      FactoryGirl.create(:admin, user: user) if not admin
      sign_in user
    end
  end

  def login_user(user = nil)
    before(:each) do
      # @request.env["devise.mapping"] = Devise.mappings[:user]
      user ||= User.find_by(email: '[email protected]')
      user ||= FactoryGirl.create(:user, email: '[email protected]', uid: 'default_user.controller.spec')
      sign_in user
    end
  end
end

the rails_helper.rb

RSpec.configure do |config|
  # for loading devise in test
  config.include Devise::TestHelpers, :type => :controller
  config.extend ControllerMacros, :type => :controller
end

Upvotes: 1

Views: 3493

Answers (1)

nicolas
nicolas

Reputation: 920

Your login_user method is run when the test suite load, you should put it in a before :each block to run it once for each test.

describe "GET index" do 
  before do
    login_user
  end
  it 'blabla' do
    get :index
    expect(response).to redirect_to(root_path)
  end
end

PS : Don't know what you do in your login_user method, but Devise have some nice helpers you can include as follow

#rails_helper.rb
RSpec.configure do |config|
  config.include Devise::TestHelpers, type: :controller
end

#then in you test 
before do
  sign_in user_instance
end

UPDATE from comment

If you have multiple type of user / devise login entry, maybe try to specify the devise mapping you're trying to sign in the user to , as follow :

sign_in :user, user_instance
sign_in :admin, admin_user_instance      

Upvotes: 1

Related Questions