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
0 comments:
Post a Comment