For some reason, I can not get the devise helper method sign_in to work. current_user keeps on being null. Any idea what the problem could be?
Test:
before :each do
#user = FactoryGirl.create :user
sign_in #user
end
describe "GET index" do
it "assigns all subscribers as #subscribers" do
subscriber = #user.subscribers.create! valid_attributes
get :index
assigns(:subscribers).should eq([subscriber])
end
end
Implementation:
def index
#subscribers = current_user.subscribers.all <------- ERROR
respond_to do |format|
format.html # index.html.erb
format.json { render json: #subscribers }
end
end
Error:
NoMethodError:
undefined method `subscribers' for nil:NilClass
Any help is appreciated. Thanks!
If you include the Confirmable module in your User model (or other devise-authenticatable model), then the test #user you create must be confirmed for the sign_in to take effect:
before :each do
#user = FactoryGirl.create :user
#user.confirm!
sign_in #user
end
(I see that this wasn't your issue, but perhaps another reader shall benefit from it.)
Looks like you solved this, judging by your code. I have had this happen before, and for some reason it gets me every time.
The rspec/rails scaffold for controller specs won't work with Devise::TestHelpers out of the box.
get :index, {}, valid_session
The valid_session call overwrites the session stuff that Devise sets up. Remove it:
get :index, {}
This should work!
For versions of Devise 4.2.0+, the Devise::TestHelpers have been deprecated. Instead, Devise::Test::ControllerHelpers should be used.
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
end
changelog
For the spec, make sure to include Devise::TestHelpers. To make it easy, in my spec/spec_helper.rb, I have:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
which automatically includes it for all controller specs.
Also, you need to do this to get sign_in to work:
#request.env["devise.mapping"] = Devise.mappings[:user]
get :new
It is probably best to add #request.env["devise.mapping"] = Devise.mappings[:user] to your before(:each). (Note you can do this in your config if you don't want to do this for every controller).
For the current_user part, make sure you have a model User, where you call devise
class User < ActiveRecord::Base
# call devise to define user_signed_in? and current_user
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
# though you don't have to include all these modules
end
Devise uses the call in the User model to define user_signed_in? and current_user in your controllers. The reason is that if you have:
class Admin < ActiveRecord::Base
devise
end
then Devise will have methods admin_signed_in? and current_admin defined.
I encountered this problem when trying to test that an SSO endpoint I was writing was creating an session for a user. Since it only applied to one test, I just needed to add the following block before my test
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user, :email => email, :password => "password")
user.confirm!
end
it "should create and session for the user and redirect to home page" do
Related
I'm having custom controller
class Users::SessionsController < Devise::SessionsController
# POST /resource/sign_in
def create
binding.pry
super
end
end
routes added
devise_for :users, controllers: { sessions: "users/sessions" }
and it works during signin using browser. But inside controller test breakpoint inside create is not being hit:
RSpec.describe Users::SessionsController, type: :controller do
describe 'POST #create' do
context 'pending activation user with expired password' do
it 'could not login' do
user = create :operator_user, status: User.statuses[:activation_pending], password_changed_at: (1.day + 1.second).ago
#request.env['devise.mapping'] = Devise.mappings[:user]
sign_in user
user.reload
expect(user).to be_locked
end
end
end
end
RSpec.configure do |config|
#...
# Devise methods
config.include Devise::TestHelpers, type: :controller
# ...
end
I expect expression
sign_in user
to fall into create method that I've overrided. What am I doing wrong?
ps: it even falls into standard devise SessionsController#create
You have to send request to controller by using post :create, params: {...} inside your example instead of sign_in user
I have a controller action which uses devise's "user_signed_in?" as a condition. The action looks like this:
def show
if user_signed_in?
#do stuff
end
end
But when I am testing this action in RSpec, the tests are failing because the code inside the if block never gets executed. Is there any way to stub the "user_signed_in?" method?
You could mock user_signed_in? method like this:
before do
allow_any_instance_of(Devise::Controllers::Helpers).to receive(:user_signed_in?).and_return(false)
end
The code is not executed because the user is probably not signed in.
To sign in a devise user in rspec, have this in spec/support/controller_macros.rb.
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! #if you are using the "confirmable" module
sign_in user
end
end
end
In spec/rails_helper.rb have this:
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
Then in your controller spec:
describe MyController do
login_user
....
end
I am trying to spec the following.
I need to return all entities that are linked to the logged in user. Subsequently I need to create the user before the fact and then ensure that the specific user is logged in. I am struggling to achieve this with controller macros. My specs are failing as follows
1) Yougov::Surveys::ProfilesController GET :index returns all profiles linked to the loged in user with the same country and client as the linked survey
Failure/Error: sign_in user
RuntimeError:
Could not find a valid mapping for nil
# /Users/donovan.thomson/.rvm/gems/ruby-2.2.2#insight-app/gems/devise-2.2.8/lib/devise/mapping.rb:42:in `find_scope!'
# /Users/donovan.thomson/.rvm/gems/ruby-2.2.2#insight-app/gems/devise-2.2.8/lib/devise/test_helpers.rb:46:in `sign_in'
# ./spec/support/controller_macros.rb:17:in `block in login_specific_user'
So a basic scaffolding of my controller looks as follows :
class ProfilesController < ApplicationController
def index
render json: Profile.where(user_id: current_user.id)
end
end
I assume this means the user is not being logged in as I would expect
My spec is as follows
require 'spec_helper'
describe ProfilesController, type: :controller do
before do
#user = FactoryGirl.create :user
#profile = FactoryGirl.create :profile, user: #user
FactoryGirl.create :profile
end
describe "GET :index" do
login_specific_user(#user)
it "returns all profiles linked to the loged in user with the same country and client as the linked survey" do
get :index
expect(response.body).to eq(#profile.to_json)
end
end
end
My controller macro's are as follows:
module ControllerMacros
def login_admin
before :each do
sign_in ControllerMacros.get_user(#request, :admin, :admin_user)
end
end
def login_user
before :each do
sign_in ControllerMacros.get_user(#request, :user)
end
end
def login_specific_user(user)
before :each do
sign_in user
end
end
class << self
def get_user(req, mapping, type=mapping)
req.env["devise.mapping"] = Devise.mappings[mapping]
user = FactoryGirl.create(type)
user.confirm!
user
end
end
end
I solved this by not using controller macros and just adding the following to my before block
before do
#user = FactoryGirl.create :user
#user.confirm!
sign_in #user
end
I have a controller that depends on the user being authenticated. So it looks like this
class PlansController < ApplicationController
before_action :authenticate_user!
def create
puts "here"
if user_signed_in?
puts "true"
else
puts "false"
end
end
end
My controller tests are working just fine when teh user IS signed in, i.e., when I'm writing something like this:
require 'rails_helper'
require 'devise'
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
describe "create action" do
before do
#user = User.create(...)
sign_in :user, #user
end
it "should puts here and then true" do
post :create
# => here
# => true
end
end
But I'd also like to test what happens in the else statement. Not sure how to do this, it fundamentally doesn't even put the here. Is it possible to test this? Or should I just leave and let Devise be?
describe "create action" do
before do
#user = User.create(...)
# do not sign in user (note I have also tried to do a sign_in and then sign_out, same result)
end
it "should puts here and then true" do
post :create
# => nothing is put, not even the first here!
# => no real "error" either, just a test failure
end
end
The before_action :authenticate_user! will immediately redirect you to the default sign-in page, if the user isn't signed in, skipping the create action altogether.
The if user_signed_in? statement is moot in this case, because the user will always be signed in when that code has the chance to run.
If plans can be created with or without an authenticated user, remove the before_action line.
i create a customized devise registration controller and i want to test it with rspec.
I've tried it with a very simple test :
it "creates a new parent" do
Parent.should receive(:new)
post :create
end
but i get this exception:
Failures:
1) Parent::RegistrationsController POST create creates a new parent
Failure/Error: post :create, { :commit => "Daftar",
uncaught throw `warden'
# /home/starqle/.rvm/gems/ree-1.8.7-2010.02/gems/devise-1.1.3/lib/devise/hooks/timeoutable.rb:16:in `throw'
# /home/starqle/.rvm/gems/ree-1.8.7-2010.02/gems/devise-1.1.3/lib/devise/hooks/timeoutable.rb:16
I already put this line within my test:
describe Parent::RegistrationsController do
include Devise::TestHelpers
end
I also already put this line:
request.env["devise_mapping"] = Devise.mappings[:parent]
anybody have ideas to solve this problem?
My previous answer is a little confusing. sorry.
Updated answer: root cause is user is not "confirmed" before "sign in".
#user.confirm!
sign_in #user
then everything is fine.
I am fresher in ruby.
I am using rails 3 with devise and factory girl.
I was searching for how to authenticate user for rspec.
I was stucked at before_filter: authenticate_user! in controller.
Finally I got solution (thanks to Siwei Shen)
What I am doing is
include TestHelpers in spec/spec_helper.rb
2.
require 'spec_helper'
describe StudentsController do
before(:each) do
#user = Factory.create(:user) #:user from factory girl with admin privilages
#request.env['devise.mapping'] = :user
#user.confirm!
sign_in #user
end
it "can get index of student" do
get :index
response.should be_suclogin_as #user
end
it "can create student" do
#in student model : validates :name, :presence=> true
post :create, :student => {name => "student1" }
answer = Student.find_by_name("student1")
answer.name.should == "student1"
end
end