rspec controller test with devise authentication - testing

I am having problem with rspec testing controller the devise authentication.
I have a following setup
I have included
config.include Devise::TestHelpers, :type => :controller
in my spec_helper.rb
In my merchants_controller_spec.rb
describe MerchantsController do
before :each do
#user = Factory(:user)
#merchant = Factory(:merchant, :user_id => #user.id,:is_approved => false, :is_blacklisted => false)
controller.stub!(:current_user).and_return(#user)
end
describe "GET index" do
it "assigns all merchants as #merchants" do
merchant = Factory(:merchant,:is_approved => true, :is_blacklisted => false)
get :index
assigns(:merchants).should eq([merchant])
end
end
end
My merchants_controller.rb
class MerchantsController < ApplicationController
before_filter :authenticate_user!
def index
#merchants = Merchant.approved
debugger
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #merchants }
end
end
end
I have a scope approved in merchant model
scope :approved, where(:is_approved => true, :is_blacklisted => false)
Now my problem is even though i stubbed current_user and returned #user as current_user, My merchants_controller index spec is failing. But if i comment out authenticate_user! then the spec passes,
without authenticate_user! the debugger of index action is caught but with authenticate_user! debugger is not caught.
I think there is problem in subbing current_user and i am not able to figure it out.
Help me out..

Have you read through the docs on github?:
Devise includes some tests helpers for functional specs. To use them, you just need to include Devise::TestHelpers in your test class and use the sign_in and sign_out methods. Such methods have the same signature as in controllers:
sign_in :user, #user # sign_in(scope, resource)
sign_in #user # sign_in(resource)
sign_out :user # sign_out(scope)
sign_out #user # sign_out(resource)

Another alternative
RSpec.describe YourController, :type => :controller do
before do
user = FactoryGirl.create(:user)
allow(controller).to receive(:authenticate_user!).and_return(true)
allow(controller).to receive(:current_user).and_return(user)
end
# rest of the code
end

Related

Rspec Devise Sign in

I trying to sign in my admin so as to test the controller specs in rspec
I did my best to follow the instruction mention over here but it seem that I missed something because of which the admin is still not able to login resulting in my controller spec to fail
Here my code
## spec/spec_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
## spec/support/controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
#request.env['devise.mapping'] = Devise.mappings[:admin]
admin = Admin.create(first_name: 'Admin',
last_name: 'User',
email: 'admin#poaster.me',
password: 'admin1234',
password_confirmation: 'admin1234',
active: true
)
admin.confirmed_at = Time.now
admin.confirm!
sign_in admin
## Did an idependent test over here using binding.pry not sure why
# binding.pry
# session => {"warden.user.admin.key"=>[[27], "$2a$04$6KicZPZUvYxOBiMadVyIqe"]}
# request['env'].warden.user => nil
# request['env'].warden.authenticated? => false
end
end
end
The output that see in session inside my controller spec
{"flash"=>
#<ActionDispatch::Flash::FlashHash:0x007f9bee570738
#closed=false,
#flashes={:alert=>"You have to confirm your account before continuing."},
#now=nil,
#used=#<Set: {}>>}
Controller Spec
## spec/controllers/users_controller_spec.rb
login_admin
describe UsersController do
context 'GET users/index' do
it 'render users index pages' do
get :index
#binding.pry
#session => {"flash"=> #<ActionDispatch::Flash::FlashHash:0x007f9bee570738 #closed=false,#flashes={:alert=>"You have to confirm your account before continuing."},#now=nil,#used=#<Set: {}>>}
expect(response).to render_template('index')
end
end
end
Controller Code
## app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_admin!
before_filter :require_admin?, :only => [:deactivate, :activate, :index]
def index
page = params[:page] || 1
#users = User.includes(:company).where(roles_mask: 1) params[:page])
#users = #users.page(page).per(10)
end
end
Found it seem the issue was because I was doing this
admin.confirmed_at = Time.now
I turn using only
admin.confirm! plainly work out of box

Rspec controller error? expecting <"index"> but rendering with <""> or it's working?

I'm new with rspec test and maybe there are something that I dont undertand.
if can any help me, I really appreciate some help.
File Structure:
app/models/booking.rb
app/models/user.rb
app/models/role.rb
app/models/ability.rb
app/controllers/bookings_controller.rb
app/views/bookings/index.html.erb
app/views/dashboard/index.html.erb
app/spec/controllers/bookings_controller_spec.rb
I read this link with a similar problem but it isn't solved
Rspec controller error expecting <"index"> but rendering with <"">
is similar, because if I change this line:
it 'should not render index template from bookings' do
get :index
=> response.should_not render_template(:index)
end
for this other:
it 'should not render index template from bookings' do
get :index
=> response.should render_template(:index)
end
I get the same mistake that in the link
expecting <"index"> but rendering with <"">
and I don't know why?
Here's my Code:
My Spec:
describe BookingsController do
context 'as guest' do
before(:each) do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.save
#when i save, with gem CanCan i assign a default role to #user
#with the default role the user only can see the views/dashboard/index.html.erb
end
it 'should not render index template from bookings' do
get :index
response.should_not render_template(:index)
end
end
end
Controller:
class BookingsController < ApplicationController
load_and_authorize_resource
def index
...
end
def show
...
end
end
My model:
class Booking < Activerecord::Base
paginates_per 20
def
...
end
def
...
end
end
User:
Class User < ActiveRecord::Base
after_save :set_default_role
rolify
.
.
.
.
def set_default_role
self.add_role :default
end
end
Role:
class Role < ActiveRecord::Base
ROLES = {"admin" => "Admin", "default" => "Default"}
.
.
.
.
scopify
end
Ability:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :data_consistency
can :read, Booking
end
end
end
CanCan authorizes model access not controller actions. For most other actions these two are more or less the same thing, but not for the index. On the index action CanCan adds a scope to the query for records that includes your authorization restrictions.
What this means is that your guest user will simply not be able to see any records, but the view will still render.
What you want is authentication (ie Devise) and use it from a before_filter in each controller that requires an authenticated user to access.
class BookingsController < ApplicationController
load_and_authorize_resource # Handles authorization
before_filter !authenticate_user # Handles authentication (included with Devise)
...
end
In my case, the problem was solved in before(:each) block!
My code works like this:
before :each do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.confirm!
sign_in #user
end

Rails respond_with behavior for deleted resource?

I'm using respond_to and respond_with pair in Rails 3 to respond to a DELETE #destroy request. The destroy action is defined in UsersController. It destroy the User specified by params[:id] and respond with a JS template. However, the request keeps failing in RSpec test and I'm not really sure how to fix it.
Here's a snippet for my UsersController:
class UsersController < ApplicationController
respond_to :html, only: [:index, :show, :new, :edit]
respond_to :js, only: [:create, :update, :delete]
...
def destroy
#user = User.find(params[:id])
#user.destroy
respond_with #user
end
end
And here is the test that keeps failing:
require 'spec_helper'
describe UsersController do
...
describe "DELETE #destroy" do
before { #user = create(:user); delete :destroy, id: #user.id, format: :js }
it "return HTTP success" do
response.should be_success
end
it "respond with JS content" do
response.content_type.should == 'text/javascript'
end
end
end
The test "respond with JS content" does succeed, however the test "return HTTP successs" fails. When I use a debugger to check response.code, it is 406. I'm expecting 2xx since the deletion succeed. Is this a normal Rails behavior or do I have something wrong with my code?
Seems you messed up REST delete and controller's destroy. Rails responder knows nothing about :delete action, change
respond_to :js, only: [:create, :update, :delete]
to
respond_to :js, only: [:create, :update, :destroy]
or pass a block to the responder, instead of
respond_with #user
use
respond_with #user do |format|
format.js
end

Rails 3 NoMethod Error - undefined method for profile in Profiles#edit with CanCan

In my Rails 3 app, I'm getting redirected to login during my signup process. The steps to signup are supposed to be:
User creates User and Profile
Upon saving user, user is logged into the app and redirected to Profiles#edit (/signup/join)
Upon saving profile, user is redirect to Profiles#show (/profiles/:id)
I'm getting redirected to /login after step 1, and I'm seeing a 302 error after the redirect. If I comment out my before_filter :authenticate in profiles_controller.rb and redo the steps above I don't get redirected out of /signup/join but I get the following error:
NoMethodError in ProfilesController#edit
undefined method `profile' for nil:NilClass
I'm pointed to the first line of my Profiles#edit action:
def edit
#profile = user.profile
if #profile.higher_ed?
higher_ed = HigherEd.find_or_create_by_name(:name => #profile.higher_ed)
end
if #profile.employer?
employer = Employer.find_or_create_by_name(:name => #profile.employer)
end
render :layout => "join_form"
end
I've been making an attempt to implement CanCan in my app, so I thought that was the cause. However I commented out my entire ability.rb file and the problem persists. I'd obviously like to figure out how to fix this without commenting out the before_filter. So if anyone has an idea I'd greatly appreciate it. Since I'm dealing with CanCan which depends on a current_user, I'll start with the definition of current_user in my application_controller.rb:
protected
# Returns the currently logged in user or nil if there isn't one
def current_user
return unless session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
#current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
# Make current_user available in templates as a helper
helper_method :current_user
Here's my users_controller.rb:
class UsersController < ApplicationController
before_filter :authenticate, :only => [:edit, :update, :index]
layout "application"
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to current_user.profile
end
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to join_path, :notice => 'User successfully added.'
UserMailer.registration_confirmation(#user).deliver
else
render :action => 'new'
end
end
My profiles_controller.rb:
class ProfilesController < ApplicationController
#before_filter :authenticate, :only => [:edit, :update]
helper_method :find_or_create_group
layout "application", :except => [:edit, :show]
def new
#profile = Profile.new(params[:profile])
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to #user.profile, :notice => 'User successfully added.'
else
render :new
end
if #profile.higher_ed?
HigherEd.find_or_create_by_name(:name => #profile.higher_ed)
end
if #profile.employer?
Employer.find_or_create_by_name(:name => #profile.employer)
end
if #profile.job_title?
JobTitle.find_or_create_by_name(:name => #profile.job_title)
end
if #profile.high_school?
HighSchool.find_or_create_by_name(:name => #profile.high_school)
end
end
def user
#user = current_user
end
def edit
#profile = user.profile
if #profile.higher_ed?
higher_ed = HigherEd.find_or_create_by_name(:name => #profile.higher_ed)
end
if #profile.employer?
employer = Employer.find_or_create_by_name(:name => #profile.employer)
end
render :layout => "join_form"
end
My sessions_controller.rb:
class SessionsController < ApplicationController
def new
end
def create
if user = User.authenticate(params[:email].downcase, params[:password])
session[:user_id] = user.id
cookies.permanent[:auth_token] = user.auth_token
if user.profile.higher_ed?
redirect_to user.profile, :notice => "Logged in successfully"
else
redirect_to join_path, :notice => "Logged in successfully"
end
else
flash.now[:alert] = "Invalid login/password. Try again!"
render :action => 'new'
end
end
def destroy
reset_session
cookies.delete(:auth_token)
redirect_to root_path, :notice => "You successfully logged out"
end
end
My ability.rb for CanCan:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new guest user
if user.role? :admin
can :manage, :all
else
can :manage, :all
end
end
end
My routes.rb:
match "/signup/join" => "profiles#edit", :as => 'join'
#profile = user.profile
Try changing the above line to
#profile = #current_user.profile
or
#profile = current_user.profile
The issue has nothing to do with cancan, rather it has to do with "user" being nil in your controller.
I got it working by reworking my current_user logic. It's now:
def current_user
#current_user ||= lookup_user
end
def lookup_user
if cookies[:auth_token]
User.find_by_auth_token!(cookies[:auth_token])
elsif session[:user_id]
User.find_by_id(session[:user_id])
end
end
That seems to have done the trick.

Rspec, CanCan and Devise

I am starting a project and i would like to be able to test everything :)
And i have some problems with CanCan and devise.
For exemple, I have a controller Contacts. Everybody can view and everybody (excepts banned people) can create contact.
#app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
load_and_authorize_resource
def index
#contact = Contact.new
end
def create
#contact = Contact.new(params[:contact])
if #contact.save
respond_to do |f|
f.html { redirect_to root_path, :notice => 'Thanks'}
end
else
respond_to do |f|
f.html { render :action => :index }
end
end
end
end
The code work, but I don't how to test the controller.
I tried this. This works if I comment the load_and_authorize_resource line.
#spec/controllers/contacts_controller_spec.rb
require 'spec_helper'
describe ContactsController do
def mock_contact(stubs={})
(#mock_ak_config ||= mock_model(Contact).as_null_object).tap do |contact|
contact.stub(stubs) unless stubs.empty?
end
end
before (:each) do
# #user = Factory.create(:user)
# sign_in #user
# #ability = Ability.new(#user)
#ability = Object.new
#ability.extend(CanCan::Ability)
#controller.stubs(:current_ability).returns(#ability)
end
describe "GET index" do
it "assigns a new contact as #contact" do
#ability.can :read, Contact
Contact.stub(:new) { mock_contact }
get :index
assigns(:contact).should be(mock_contact)
end
end
describe "POST create" do
describe "with valid params" do
it "assigns a newly created contact as #contact" do
#ability.can :create, Contact
Contact.stub(:new).with({'these' => 'params'}) { mock_contact(:save => true) }
post :create, :contact => {'these' => 'params'}
assigns(:contact).should be(mock_contact)
end
it "redirects to the index of contacts" do
#ability.can :create, Contact
Contact.stub(:new) { mock_contact(:save => true) }
post :create, :contact => {}
response.should redirect_to(root_url)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved contact as #contact" do
#ability.can :create, Contact
Contact.stub(:new).with({'these' => 'params'}) { mock_contact(:save => false) }
post :create, :contact => {'these' => 'params'}
assigns(:contact).should be(mock_contact)
end
it "re-renders the 'new' template" do
#ability.can :create, Contact
Contact.stub(:new) { mock_contact(:save => false) }
post :create, :contact => {}
response.should render_template("index")
end
end
end
end
But these tests totally failed ....
I saw nothing on the web ... :(
So, if you can advise me on the way i have to follow, i would be glad to ear you :)
CanCan does not call Contact.new(params[:contact]). Instead it calls contact.attributes = params[:contact] later after it has applied some initial attributes based on the current ability permissions.
See Issue #176 for details on this and an alternative solution. I plan to get this fixed in CanCan version 1.5 if not sooner.