How do I simulate a login with RSpec?
The answer depends on your authentication implementation. Normally, when a user logs in, you'll set a session variable to remember that user, something like session[:user_id]
. Your controllers will check for a login in a before_filter
and redirect if no such session variable exists. I assume you're already doing something like this.
To get this working in your tests, you have to manually insert the user information into the session. Here's part of what we use at work:
# spec/support/spec_test_helper.rbmodule SpecTestHelper def login_admin login(:admin) end def login(user) user = User.where(:login => user.to_s).first if user.is_a?(Symbol) request.session[:user] = user.id end def current_user User.find(request.session[:user]) endend# spec/spec_helper.rbRSpec.configure do |config| config.include SpecTestHelper, :type => :controllerend
Now in any of our controller examples, we can call login(some_user)
to simulate logging in as that user.
I should also mention that it looks like you're doing integration testing in this controller test. As a rule, your controller tests should only be simulating requests to individual controller actions, like:
it 'should be successful' do get :index response.should be_successend
This specifically tests a single controller action, which is what you want in a set of controller tests. Then you can use Capybara/Cucumber for end-to-end integration testing of forms, views, and controllers.
Add helper file in spec/support/controller_helpers.rb and copy content below
module ControllerHelpers def sign_in(user) if user.nil? allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user}) allow(controller).to receive(:current_user).and_return(nil) else allow(request.env['warden']).to receive(:authenticate!).and_return(user) allow(controller).to receive(:current_user).and_return(user) end end end
Now add following lines in spec/rails_helper.rb or spec/spec_helper.rb file
require 'support/controller_helpers'RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller config.include ControllerHelpers, :type => :controller end
Now in your controller spec file.
describe "GET #index" do before :each do @user=create(:user) sign_in @user end ...end
As I couldn't make @Brandan's answer work, but based on it and on this post, I've came to this solution:
# spec/support/rails_helper.rbDir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # Add this at top of file...include ControllerMacros # Add at bottom of file
And
# spec/support/controller_macros.rbmodule ControllerMacros def login_as_admin admin = FactoryGirl.create(:user_admin) login_as(admin) end def login_as(user) request.session[:user_id] = user.id endend
Then on your tests you can use:
it "works" do login_as(FactoryGirl.create(:user)) expect(request.session[:user_id]).not_to be_nilend