So I was just trying to call a log_in method from user controller in the RSpec as
it "should get the index page" do
#user = User.new({ :email => "employee#test.com" })
log_in(#user)
get 'index'
response.should be_success
end
The result I got is like
1) EmployeesController GET 'index' should get the index page
Failure/Error: log_in(user)
NoMethodError:
undefined method `log_in' for #<RSpec::Core::ExampleGroup::Nested_1:0x4ac0328>
# ./spec/controllers/employees_controller_spec.rb:11:in `user_log_in'
# ./spec/controllers/employees_controller_spec.rb:16:in `block (2 levels) in <top (required)>'
Can someone help me out? Thanks
Edited March 11th, 2011
Here is the log_in method which is in UserController
def log_in(user)
session[:current_user] = user.id
end
If you want to call a method on the controller in an RSpec controller test, you could use the following.
subject.send(:log_in,#user)
It should call the method. I dont know if this is really a best practice. A better method would be to stub the logged_in method as BurmajaM suggested.
Why don't you stub logged_in? or whatever your method is. Logging in is not target of this spec, so stub it! Here's simple example how I spec controller action that has before_filter:
class MyController < ApplicationController
before_filter :logged_in?
def index
end
end
describe MyController do
describe "GET 'index'" do
context "when not logged in"
# you want to be sure that before_filter is executed
it "requires authentication" do
controller.expects :logged_in?
get 'index'
end
# you don't want to spec that it will redirect you to login_path
# because that spec belongs to #logged_in? method specs
end
context "when authenticated" do
before(:each) { controller.stubs :logged_in? }
it "renders :index template" do
get 'index'
should render_template(:index)
end
it "spec other things your action does when user is logged in"
end
end
end
Related
I'm currently attempting my first unit test and I'm receiving the following errors
Failures:
1) StaticPagesController GET #index responds successfully with an HTTP 200 status code
Failure/Error: get :index
NoMethodError:
undefined method `authenticate!' for nil:NilClass
# /Users/danielmayle/.rvm/gems/ruby-2.2.1/gems/devise-3.5.2/lib/devise/controllers/helpers.rb:112:in `authenticate_user!'
# ./spec/static_pages_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
2) StaticPagesController GET #index renders the index template
Failure/Error: get :index
NoMethodError:
undefined method `authenticate!' for nil:NilClass
# /Users/danielmayle/.rvm/gems/ruby-2.2.1/gems/devise-3.5.2/lib/devise/controllers/helpers.rb:112:in `authenticate_user!'
# ./spec/static_pages_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
Here is my unit test code:
require 'rails_helper'
describe StaticPagesController, :type => :controller do
context "GET #index" do
before do
get :index
end
it "responds successfully with an HTTP 200 status code" do
expect(response).to be_success
expect(response).to have_http_status(200)
end
it "renders the index template" do
expect(response).to render_template("index")
end
end
end
And here is my static_controller.rb code:
class StaticPagesController < ApplicationController
def index
end
def landing_page
#featured_product = Product.first
end
def thank_you
#name = params[:name]
#email = params[:email]
#message = params[:message]
UserMailer.contact_form(#email, #name, #message).deliver_now
end
end
Why do are these errors coming up and how do I fix the problem? I've only been coding for a few months so any assistance would be much appreciated. Thanks :)
Update
Here is my application controller
class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :configure_permitted_parameters, if: :devise_controller?
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
redirect_to main_app.root_url, :alert => exception.message
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
end
Here is my user_mailer code as well
class UserMailer < ActionMailer::Base
default from: "dmayle012#gmail.com"
def contact_form(email, name, message)
#message = message
mail(:from => email,
:to => 'dmayle012#gmail.com',
:subject => "New ActionMail Message from #{name}")
end
end
Your problem is this line:
before_action :authenticate_user!
This makes devise to check authorisation for current_user, which is nil in your test. There are two ways to fix it depending on what your requirements are.
Firstly, if you want any internet user to be able to view your static pages without login, add:
skip_before_action :authenticate_user!
in your StaticPagesController. This will tell devise that you don't require current_user to be allowed to view the page (not really - it doesn't tell devise anything, it just not asking it to authorise user).
Second option - you need user to be logged in to view those pages. In this case you need to create fake session before you start your test using some helper methods provided by devise. This is very easy and well documented process, you can find the steps here: https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)#controller-specs. Not adding those steps in the answer as I believe you will go with first case (since every page requires some pages available without the session, at least for the login page).
Devise has some helper methods specifically for this. Here is how to setup your controller spec to get past your undefined method authenticate_user! for nil error
First you need to include them in your rails_helper.rb like this
# spec/rails_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end
And then you can use the helper like this in your static pages controller
# spec/controllers/static_pages_controller_spec.rb
describe StaticPagesController, :type => :controller do
context "GET #index" do
before :each do
user = FactoryGirl.create(:user, approved: true)
sign_in :user, user
get :index
end
...
end
end
Hi I am implementing a method to delete a user account in my web application. My controller:
class UsersController < ApplicationController
before_filter :set_current_user
def user_params
params.require(:user).permit(:user_id, :first_name, :last_name, :email, :password, :password_confirmation)
end
def delete_account
#user = User.find_by_id(params[:id])
if #user.present?
#user.destroy
flash[:notice] = "User Account Deleted."
end
redirect_to root_path
end
def destroy
User.delete(:user_id)
redirect_to root_path
end
end
My rspec:
require 'spec_helper'
require 'rails_helper'
require'factory_girl'
describe UsersController do
describe "delete account" do
before :each do
#fake_results = FactoryGirl.create(:user)
end
it "should call the model method that find the user" do
expect(User).to receive(:find).with(params[:id]).and_return (#fake_results)
end
it "should destroy the user account from the database" do
expect{delete :destroy, id: #fake_results}.to change(User, :count).by(-1)
end
it "should redirect_to the home page" do
expect(response).to render_template(:home)
end
end
end
The first error is
Failure/Error: expect(User).to receive(:find).with(params[:id]).and_return (#fake_results)
NameError:undefined local variable or method `params' for #<RSpec::ExampleGroups::UsersController::DeleteAccount:0x00000007032e18>
I know what this error means but I don't know how to correct it. How can I pass the user id from the controller to rspec?
The second error is:
Failure/Error: expect(response).to render_template(:home)
expecting <"home"> but rendering with <[]>
I think there is something wrong with my controller method. It should redirect to the home page but it doesn't.
params is not available in your tests, it's available in your controller.
Looks like you create a test user in your test:
#fake_results = FactoryGirl.create(:user)
Then, you can use the id of this test user (#fake_results.id) instead of trying to use params[:id]:
expect(User).to receive(:find).with(#fake_results.id).and_return (#fake_results)
Although, you may want to change the name from #fake_results to something more meaningful e.g. test_user or so.
However, this should fix both of your problems as your second problem is there because of the first problem. As it's failing to delete the user in the first place, it's not being redirected to the root path and hence the home template is not rendering.
i want to to write a controler test to check that on a successful sign in, a user is redirected to a certain page. the current test I have at the moment is returning a 200.
require 'rails_helper'
RSpec.describe Admin::EntriesController, :type => :controller do
setup_factories
describe "after login" do
it "should redirect to pending after logged in" do
sign_in admin
expect(response).to redirect_to('admin/entries/pending')
end
end
end
which returns
Failure/Error: expect(response).to redirect_to('admin/entries/pending')
Expected response to be a <redirect>, but was <200>
the relevant controller
class AdminController < Devise::RegistrationsController
before_filter :authenticate_admin!
protected
def after_sign_in_path_for(admin)
pending_admin_entries_path
end
end
am I attempting this is in the right way, where am i going wrong?
thanks
sign_in user in RSpec doesn't make a request so you cannot test the redirection.
For after_sign_in_path, you can test like this:
it 'redirects user to pending admin entries path' do
expect(controller.after_sign_in_path(user)).to eq pending_admin_entries_path
end
I have a simple user login system:
module SessionsHelper
def logged_in?
current_user.present?
end
def current_user
#current_user ||= begin
if session[:current_user_id]
User.find(session[:current_user_id]) rescue nil
end
end
end
end
which I include in ApplicationController:
class ApplicationController < ActionController::Base
include SessionsHelper
helper SessionsHelper
I am trying to test the banners controller:
require 'spec_helper'
describe Admin::BannersController do
describe 'POST create' do
let(:user){ create(:user_admin) }
before do
controller.stub(:current_user){ user }
end
it "create action should render new template when model is invalid" do
Banner.any_instance.stubs(:valid?).returns(false)
post :create
response.should render_template(:new)
end
:user_admin is the properly set up Factory Girl admin user.
However the test still says: You are not authorized to access this page.
This is from Cancan.
Did I not stub it properly? Thanks.
def fake_user
user = FactoryGirl.build(:user, :id => 100000 + rand(100000))
stub(controller).current_user { user }
user
end
I have a hard time testing my controller with before_filters, exceptions and some mocking and stubing.
Here is the controller:
before_filter :get_subject, :only => [:show, :edit, :update, :destroy, :update_field]
before_filter :user_has_to_belongs_to_subject_company, :only => [:show, :edit, :update, :destroy, :update_field]
def show
#messages = #subject.get_user_messages(current_user)
end
private
def get_subject
#subject = Subject.find(params[:id])
end
def user_has_to_belongs_to_subject_company
unless #current_user.company.eql?(#subject.company)
raise "Error: current_user does not belongs to subject's company"
end
end
And here is my spec file:
require 'spec_helper'
describe SubjectsController do
describe "for signed users" do
before(:each) do
#current_user = Factory(:user)
sign_in #current_user
end
describe "for user belonging to subject's company" do
before(:each) do
#subject = mock_model(Subject)
Subject.stub!(:find).with(#subject).and_return(#subject)
#current_user.stub_chain(:company, :eql?).and_return(true)
#subject.stub!(:company)
end
it "should not raise an exception" do
expect { get :show, :id => #subject }.to_not raise_error
end
end
describe "for user not belonging to subject's company" do
before(:each) do
#subject = mock_model(Subject)
Subject.stub!(:find).with(#subject).and_return(#subject)
#current_user.stub_chain(:company, :eql?).and_return(false)
#subject.stub!(:company)
end
it "should raise an exception" do
expect { get :show, :id => #subject }.to raise_error
end
end
end
end
And finally, here is the error message:
SubjectsController for signed users for user belonging to subject's company should not raise an exception
Failure/Error: expect { get :show, :id => #subject }.to_not raise_error
expected no Exception, got #<RuntimeError: Error: current_user does not belongs to subject's company>
# ./spec/controllers/subjects_controller_spec.rb:19:in `block (4 levels) in <top (required)>'
Thx for helping!
I'm not seeing the problem, but here's a refactoring suggestion. If you find yourself using more mocks and stubs that usual, maybe it's time to reconsider your interfaces. In this case, you can make your controller skinnier and you model fatter.
# subjects_controller_spec.rb
describe "for user belonging to subject's company" do
before(:each) do
#subject = mock_model(Subject, :verify_user => true)
Subject.stub!(:find).with(#subject).and_return(#subject)
end
# subjects_controller.b
def user_has_to_belongs_to_subject_company
#subject.verify_user(#current_user)
end
# subject.rb
class Subject
def verify_user(user)
unless user.company.eql?(company)
raise "Error: current_user does not belongs to subject's company"
end
What happens if you delete the # in front of #current_user in
def user_has_to_belongs_to_subject_company
unless #current_user.company.eql?(#subject.company)
to get
def user_has_to_belongs_to_subject_company
unless current_user.company.eql?(#subject.company)
And in your specs, do controller.stub!(:current_user).and_return #current_user
I think the problem is one of scope - #current_user in your tests is different to #current_user in your controller. Really depends on how "sign_in #current_user" is implemented.
Also, instead of raising an exception, perhaps your before_filter could redirect the user to another page and set flash[:error]? The before filter is the right place to handle this situation, so it shouldn't raise an exception that would have to be rescued somewhere else (or if not, it would display a 500 page to the user).