Tuesday, November 21, 2017

Stub authentication in request specs

Leave a Comment

I'm looking for the way to do this but in request specs. I need to log in and log out a double or instance_double to Devise instead of an actual ActiveModel/ActiveRecord.

By using the code in the wiki page:

module RequestSpecHelpers     def sign_in(user = double('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 

I get this error: undefined method 'env' for nil:NilClass

I saw this question and this wiki, but if I want to use doubles of the user those two don't work. I was using the last one, works fine with a real user but with a double it doesn't log it in.

The tests:

RSpec.describe 'new shipment', type: :request do   describe 'authenticated as user' do     before do       @user = double(:user, id: 1, email: 'user@gmail.com', password: 'password',                       id_card: '4163649-1', first_name: 'Jane', last_name: 'Doe')        sign_in @user     end   end end 

If I include:

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

I get this error:

Failure/Error: @request.env['action_controller.instance'] = @controller       NoMethodError:        undefined method `env' for nil:NilClass      # /root/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/devise-4.3.0/lib/devise/test/controller_helpers.rb:40:in `setup_controller_for_warden' 

Problem with Frederick Cheung answer

If I do that the login_asmethod doesn't fail but it doesn't really log the user in. So when I try to access a path that has a before_action :authenticate_user! callback it fails.

Here is my code based on his answer:

require 'rails_helper'  RSpec.describe 'new shipment', type: :request do   describe 'authenticated as user' do     include Warden::Test::Helpers      before(:each) do       Warden.test_mode!       #stub more methods as needed by the pages you are testing       user = instance_double(User, to_key: 1, authenticatable_salt: 'example')       login_as(user, scope: 'user')     end      it 'returns 200 Ok' do       get new_shipment_path       expect(response).to have_http_status(:ok)     end   end end 

And this is the response when running rspec:

 1) new shipment authenticated as user returns 200 Ok      Failure/Error: expect(response).to have_http_status(:ok)        expected the response to have status code :ok (200) but it was :found (302)      # ./spec/requests/shipments_requests_spec.rb:41:in `block (3 levels) in <top (required)>' 

As you can see instead of allowing me to access the path it redirects me, this is the usual behavior when the user is not allowed to access the path.

It I change the instance_double for a real User saved in the database this approach works correctly:

# only changed this line in the before hook user = User.create(email: 'user@gmail.com', password: 'password',id_card: '4163649-1', first_name: 'Jane', last_name: 'Doe') 

Result:

Finished in 3.23 seconds (files took 33.47 seconds to load) 1 example, 0 failures 

1 Answers

Answers 1

It sounds like you're using Devise 3.x ( since Devise::TestHelpers was renamed in devise 4), Devise::TestHelpers is only designed to work with controller specs.

If you can upgrade to devise 4, it has separate helpers for request specs and controller tests. This is just a very thin wrapper around what warden provides, which hides all the messing around with env.

There are some extra complications when using a double - you need to stub out various methods devise calls that you might not realise.

The following worked for me

describe 'example' do   include Warden::Test::Helpers    before(:each) do     Warden.test_mode!     #stub more methods as needed by the pages you are testing     user = instance_double(User, to_key: 1, authenticatable_salt: 'example')     login_as(user, scope: 'user')   end end 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment