Devise, undefined local variable or method current_user - ruby-on-rails-3

I have Project controller and rspec file for it. I'm usig current_user helper in this way:
# projects_controller_spec.rb
describe "user signed in" do
before(:each) { sign_in #user }
after(:each) { sign_out #user}
describe "GET index" do
it "assigns all projects as #projects" do
project = FactoryGirl.create(:project, :user => current_user)
get :index, {}, valid_session
assigns(:projects).should eq([project])
end
end
...
end
The helpers sign_in and sign_out seem to work fine but I'v got an error on current_user:
undefined local variable or method `current_user'
What might be a problem, how to fix it?

It's normal. You should create a User instance. You could use let like this :
# projects_controller_spec.rb
describe "user signed in" do
let(:user) {FactoryGirl.create(:user)}
before(:each) { sign_in user }
after(:each) { sign_out user}
describe "GET index" do
it "assigns all projects as #projects" do
project = FactoryGirl.create(:project, :user => user)
get :index, {}, valid_session
assigns(:projects).should eq([project])
end
end
...
end
You know than the current_user is user ...
You don't need to sign out your use after each spec.

Related

Devise sign_in method throws NoMethodError in RSpec test

The Devise sign_in method is supposed to accept store: false as a second parameter, which it seems to do that fine unless I'm in RSpec and using the Devise::TestHelpers.
When I run this test from sessions_controller_spec.rb:
require 'rails_helper'
describe Api::V1::SessionsController do
before(:each) do
#user = FactoryGirl.create :user
end
...
describe 'DELETE #destroy' do
before(:each) do
sign_in #user, store: false
delete :destroy, id: #user.auth_token
end
it { should respond_with 204 }
end
end
I get this failure:
I have got the same problem. According to Devise website, there is no such method.
Try to remove the "store: false" parameter and run it again. It solved my problem.
...
describe 'DELETE #destroy' do
before(:each) do
sign_in #user
delete :destroy, id: #user.auth_token
end
it { should respond_with 204 }
...
And remember to put the following inside a file named spec/support/devise.rb or in your spec/spec_helper.rb (or spec/rails_helper.rb if you are using rspec-rails
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end
Take a look on this link

How do I emulate logging in for controller tests?

I have a SearchesController that requires a user to be logged in before it will do its thing.
I'd like to write an rspec helper function login to emulate logging in for controller tests. (NB: I will handle integration / requests specs separately.) My attempts so haven't worked: the logged_in? method in ApplicationController returns false.
The question: how do I write the 'login' helper?
Here's the RSpec controller test:
# file: spec/controllers/searches_controller_spec.rb
require 'spec_helper'
require 'controllers_helper'
describe SearchesController do
include ControllersHelper
describe "GET index" do
it 'without login renders login page' do
get :index
response.should redirect_to(login_path)
end
it 'with login finds searches belonging to user' do
me = FactoryGirl.create(:user)
my_searches = FactoryGirl.create_list(:search, 2, :user => me)
not_me = FactoryGirl.create(:user)
not_my_searches = FactoryGirl.create_list(:search, 2, :user => not_me)
login(me) # want to define this in spec/controllers_helper.rb
get :index
assigns(:searches).should =~ my_searches
end
end
end
Here's the Controller:
# file: app/controllers/searches_controller.rb
class SearchesController < ApplicationController
def index
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
else
#searches = Search.where(:user_id => current_user.id)
respond_to do |format|
format.html
format.json { render json: #searches }
end
end
end
end
And here's the ApplicationController code. Note that current_user = x has the effect of logging x in, and it's rather simple: it sets #current_user and session[:user_id].
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
protected
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
#current_user = user
session[:user_id] = user && user.id
end
def logged_in?
!!#current_user
end
def require_login
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
end
end
helper_method :current_user, :logged_in?, :require_login
end
I may have said this before, but if Stack Overflow gave badges answering one's own questions, I'd have a LOT of badges! :)
Okay, to answer this question you need to look at the documentation for ActionController::TestCase. When you do so, you'll find that it sets up bindings for:
#controller
#request
#response
So for the specific controller given in the OP, writing the login method is trivial:
# file: spec/controllers_helper.rb
module ControllersHelper
def login(user)
#controller.send(:current_user=, user)
end
end
(Did I hear someone say RTFM again? I thought so...)

TDD with RSpec and Rails: testing controller actions with no view

As I continue to learn my way around TDD with RSpec 2 and Rails 3.1, I can't seem to find a solution to this problem.
I have a Users controller with a new and create action. In my UsersController spec, I have
users_controller_spec.rb
describe "POST 'create'" do
before(:each) do
#attr = Factory.attributes_for(:user)
end
it "should assign an #user variable" do
post :create, :user => #attr
assigns[:user].should_not be_nil
assigns[:user].should be_kind_of(User)
end
end
and in my UsersController,
users_controller.rb
def create
#user = User.new(params[:user])
end
This spec is failing with
1) UsersController POST 'create' should assign an #user variable
Failure/Error: post :create, :user => #attr
ActionView::MissingTemplate:
I can continue to implement application code to get this test to pass, but I feel like this test should be passing as it is.
Any suggestions?
Your create method needs to do something. Either render a template or redirect. Since you're not telling it to redirect it's assuming that you want it to render a template but when it can't find a create.html.erb file it throws an error.
You're best bet is to do either this:
def create
#user = User.new(params[:user])
redirect_to root_url
end
or this:
def create
#user = User.new(params[:user])
render :nothing => true
end
To test rendering nothing you'll want:
expect(response).to render_template(nil)
I've come across this recently myself. It seems one possibility would be to rescue the error in your test.
it "should assign an #user variable" do
begin
post :create, :user => #attr
rescue ActionView::MissingTemplate
# This is okay because(/as long as) we test the render/redirect
# in a separate spec where we don't rescue this exception.
end
assigns[:user].should_not be_nil
assigns[:user].should be_kind_of(User)
end
I don't know how "correct" this solution is. On one hand, it definitely emphasizes the "testing one thing at a time" mentality, on the other, it seems kind of ugly.
Edit
I suppose you could make some nice wrappers in your spec helper, something like
def post?(action, params = {})
post action, params
rescue ActionView::MissingTemplate
end

Rspec2: response.should render_template("new") after invalid params fails

I am testing a controller in RSpec2 and for both my create and update actions, when passed invalid params, the controller should render either the "new" or "edit" templates respectively. It is doing that, but my test never passes.
describe "with invalid params" do
before(:each) do
User.stub(:new) { mock_user(:valid? => false, :save => false) }
end
it "re-renders the 'new' template" do
post :create, :company_id => mock_company.id
response.should render_template("new")
end
end
Results in this:
re-renders the 'new' template
expecting <"new"> but rendering with <"">
Here is the controller action:
respond_to do |format|
if #user.save
format.html {
flash[:notice] = "#{#user.full_name} was added to #{#company.name}."
redirect_to company_users_url(#company)
}
else
logger.debug #user.errors
format.html{
render :new
}
end
end
This problem also seems to be isolated to this controller. I have almost identical code running another controller and it is fine. I am not sure where the problem could be.
Update:
Here are the two mock methods
def mock_user(stubs={})
#mock_user ||= mock_model(User, stubs).as_null_object
end
def mock_company(stubs={})
(#mock_company ||= mock_model(Company).as_null_object).tap do |company|
company.stub(stubs) unless stubs.empty?
end
end
Turned out it was a problem with stubbing and CanCan. CanCan was loading the resources and uses some different methods than what I thought.

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.