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: '[email protected]', 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'
If I do that the login_as
method 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: '[email protected]', 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
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 love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With