How to set Rails cache in rspec controller test - ruby-on-rails

I want to set Rails.cache in rspec controller test, however the cache is always blank inside the controller method. What is approach to do this?
User Model
def user_token
Rails.cache.fetch(id)
end
User Controller
if current_user.user_token
#user = #account.users.find(params[:id])
#user.revoke_seat(:admin, current_user)
render :template => "/admin/users/revoke_seat"
else
render :js => "window.location.href='#{server_url}/oauth/authorize?response_type=code&client_id=#{client_id}&state=#{request.referrer}?auto_revoke_seat=true&redirect_uri=#{auth_service_callback_url}";
end
Rspec
before do
users(:admin).stub(:internal_admin?).and_return(true)
login_as :admin
Rails.cache.write(user.id, "testToken", expires_in: 2.minutes)
end
it "should redirect to authentication service to generate access token" do
expect(user).to receive(:user_token).and_return(true)
xhr :put, :revoke_seat, account_id: account.id, id: user.id
expect(response).to render_template('admin/users/revoke_seat')
expect(assigns(:account)).to eq(account)
expect(assigns(:user)).to eq(user)
end

I would change the before method as:
before do
users(:admin).stub(:internal_admin?).and_return(true)
login_as :admin
allow(Rails.cache).to receive(:fetch).with(user.id).and_return("testToken")
end

Related

Undefined local variable or method params in Rspec

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.

Devise::SessionsController#create setting session doesn't work in test

I'm trying to do integration test for our login process we have overwrite session create process like this:
module UsersDashboard
class SessionsController < Devise::SessionsController
protect_from_forgery :except => [:create, :mfa_callback, :authenticate]
def test
session[:test] = 'doodoo'
self.resource = warden.authenticate!(auth_options)
render text: 'hi'
end
# Used for User Dashboard login
def create
current_user = warden.authenticate!(auth_options)
resource = current_user
current_user.update_attribute(:mfa_authenticated, false)
# some code here
session[:channel] = 'some value'
p "session channel in create: #{session[:channel]}, #{session.class}"
redirect_url = 'some value here'
redirect_to redirect_url
end
end
and here is my test code:
require 'test_helper'
include Warden::Test::Helpers
class DashboardOkTest < ActionDispatch::IntegrationTest
test "Dashboard sign in and visiting various pages inside dashboard " do
post_via_redirect '/users_dashboard/sessions/mani'
p session[:test]
mani = users(:mani)
post_via_redirect '/users/sign_in', 'user[email]' => mani.email, 'user[password]' => '123456'
assert_response :success
assert_equal '/mfa/index', path
assert_select '#choose-mfa-method', 'Choose Your Acceptto Multi-Factor Auth Method:'
login_as mani, scope: :user
p "session: #{session[:channel]}, #{session.inspect}"
assert request.query_parameters.has_key?(:channel)
tfa = Tfa.find_by_channel(request.query_parameters[:channel])
assert tfa
tfa.update_attribute(:status, 'approved')
get "/users_dashboard/sessions/authenticate?channel=#{tfa.channel}"
end
end
as you can see in the output:
"doodoo"
"session channel in create: 123, ActionDispatch::Request::Session"
"session: , #<ActionDispatch::Request::Session:0x7fabecaa9458 not yet loaded>"
"session channel is nil"
session[:channel] doesn't work and session is not loaded yet! interesting point is I have created a test method in Session Controller as you can see and if we set session[:test] before calling self.resource = warden.authenticate!(auth_options) session works and it prints 'doodoo' but it's not the case for create method if we set session[:channel] before self.resource = warden.authenticate!(auth_options) it still doesn't work.
It looks like devise has some kind of wrapper that nullifies session after create call. and all this just happens in test while session and everything else works in development server in web.
Any idea how to set a session for test inside create method?

Can I have some feedback with rspec when writing controllers specs?

I was wondering if i could have some feedbacks with the controller spec bellow. In fact i'm new when writing specs and controller's spec are way different from model's spec ! So i'm wondering if i may not go in the wrong direction...
subjects_controller.rb
def show
#subject = Subject.find(params[:id])
if #subject.trusted?(current_user)
#messages = #subject.messages
else
#messages = #subject.messages.public
#messages = #messages + #subject.messages.where(:user_ids => current_user.id)
#messages.uniq!
end
# sort the list
#messages = #messages.sort_by(&:created_at).reverse
if !#subject.company.id == current_user.company.id
redirect_to(subjects_path, :notice => "Invalid subject")
end
end
subjects_controller_spec.rb
require 'spec_helper'
describe SubjectsController do
before(:each) do
#subject = mock_model(Subject)
end
context "for signed users" do
before(:each) do
#current_user = sign_in Factory(:user)
end
context "GET #show" do
before(:each) do
Subject.stub!(:find, #subject).and_return(#subject)
end
context "when current_user is trusted" do
before(:each) do
messages = []
company = mock_model(Company)
#subject.should_receive(:trusted?).and_return(true)
#subject.should_receive(:messages).and_return(messages)
#subject.should_receive(:company).and_return(company)
end
it "should render success" do
get :show, :id => #subject
response.should be_success
end
end
context "when current_user is not trusted" do
before(:each) do
messages = []
company = mock_model(Company)
#subject.should_receive(:trusted?).and_return(false)
#subject.should_receive(:messages).and_return(messages)
messages.should_receive(:public).and_return(messages)
#subject.should_receive(:messages).and_return(messages)
messages.should_receive(:where).and_return(messages)
#subject.should_receive(:company).and_return(company)
end
it "should render success" do
get :show, :id => #subject
response.should be_success
end
end
context "when subject's company is not equal to current_user's company" do
# I have no idea of how to implement ==
end
end
end
end
Factories.rb
Factory.define :user do |u|
u.first_name 'Test User' #
u.username 'Test User' #
u.surname 'TheTest' #
u.email 'foo#foobar.com' #
u.password 'please' #
u.confirmed_at Time.now #
end
As far as I can tell you're on the right path. The basic idea is to completely isolate your controller code from model and view in these tests. You appear to be doing that--stubbing and mocking model interaction.
Don't write RSpec controller specs at all. Use Cucumber stories instead. Much easier, and you get better coverage.

Dynamically generating shared examples in RSpec 2?

I'm trying to keep my specs DRY by creating a shared example group that performs the boilerplate checks for all admin controllers (all controllers under the Admin namespace of my project). I'm struggling to figure out how to do it, since the shared example needs providing with the information about what actions and parameters to use. It should ideally present meaningful errors if a test fails (i.e. include the details of the action it was testing).
require 'spec_helper'
shared_examples "an admin controller" do
before(:each) do
#non_admin = User.make
#admin = User.make(:admin)
end
context "as an admin user" do
#actions.each do |action, params|
specify "I should be able to access ##{action.last} via #{action.first}" do
self.active_user = #admin
send(action.first, action.last, params)
response.status.should be_ok
end
end
end
context "as a regular user" do
#actions.each do |action, params|
specify "I should be denied access to ##{action.last}" do
self.active_user = #non_admin
send(action.first, action.last, params)
response.status.should be 403
end
end
end
end
describe Admin::UserNotesController do
#user = User.make
#actions = { [:get, :index] => { :user_id => #user.id },
[:get, :new] => { :user_id => #user.id },
[:post, :create] => { :user_id => #user.id } }
it_behaves_like "an admin controller"
end
This errors for the obvious reason that #actions is not visible to the shared example group. If I use let, this is only available in the context of an example, not in the context of the describe block. Any ideas?
Here's a much cleaner way that should work:
require 'spec_helper'
shared_examples "an admin controller" do |actions|
context "as an admin user" do
actions.each_pair do |action, verb|
specify "I should be able to access ##{action} via #{verb}" do
send(verb, action, :user_id => User.make(:admin).id)
response.status.should be_ok
end
end
end
context "as a regular user" do
actions.each_pair do |action, verb|
specify "I should be denied access to ##{action}" do
send(verb, action, :user_id => User.make.id)
response.status.should be 403
end
end
end
end
describe Admin::UserNotesController do
it_behaves_like "an admin controller", {
:index => :get,
:new => :get,
:create => :post
}
end
See http://relishapp.com/rspec/rspec-core/v/2-6/dir/example-groups/shared-examples for more information

Force controller to use current_user with mocking

I am trying to specify in my RSpec tests that my controller should use current_user.projects.find() instead of Project.find() I am using the Mocha mocking framework and was trying something like this:
controller.current_user.projects.expects(:find).returns(#project)
I have already mocked out controller.stubs(:current_user).returns(#profile)
This test passes with this even when I use the Project.find() implementation. How can I test that my controller is calling off of the correct object?
Edit (adding additional code):
I have Projects and Tasks, Project have many tasks. This is the show method for displaying a task in a project that is owned by current_user
Action in the controller:
def show
#project = current_user.projects.find_by_id(params[:cardset_id])
if #project.nil?
flash[:notice] = "That project doesn't exist. Try again."
redirect_to(projects_path)
else
#task = #project.tasks.find_by_id(params[:id])
end
end
This is the test that is not checking that the cardsets method was called off the current_user object.
Current Test:
context "with get to show" do
context "with valid project" do
before(:each) do
#project = Factory(:project)
#task = Factory(:task)
#profile = #project.profile
ApplicationController.stubs(:require_user).returns(true)
controller.stubs(:current_user).returns(#profile)
Project.stubs(:find_by_id).returns(#project)
#project.tasks.stubs(:find_by_id).returns(#task)
get :show, :project_id => #project.id, :id => #task.id
end
it "should assign task" do
assigns[:task].should_not be_nil
end
it "should assign project" do
assigns[:project].should_not be_nil
end
end
context "with invalid project" do
before(:each) do
Project.stubs(:find_by_id).returns(nil)
get :show, :project_id => #project.id, :id => #task.id
end
it "should set flash" do
flash[:notice].should match(/doesn't exist/i)
end
it "should redirect" do
response.should redirect_to(cardsets_url)
end
end
end
Based on the little you've told us, I think you need:
#profile.expects(:find).returns(#project)

Resources