How can I double variable within method in rspec rails - ruby-on-rails

How can I double #user var, if I do not double it, the test will throw error of
call update_attributes of nil:NilClass
class TestController
def foo!
#user = current_user
#user.update_attribute(user_params)
end
end
RSpec.describe TestController, type: :controller do
describe "#foo" do
it "should be passed" do
#specific_user = FactoryGirl.create(:user)
allow_any_instance_of(TestController).to receive(:foo!).and_return(true)
allow_any_instance_of(TestController).to receive(#user).and_return(#specific_user)
end
end
end

The problem with your test is that it doesn't test anything. The controller test should make a request to test the method get :foo!
About stubbing, in your case current_user method can be stubbed instead:
RSpec.describe TestController, type: :controller do
describe "#foo" do
it "should be passed" do
#specific_user = FactoryGirl.create(:user)
allow(controller).to receive(:foo!).and_return(true)
allow(controller).to receive(:current_user).and_return(#specific_user)
end
end
end
And yeah, in the controller test the controller instance can be accessed by calling controller method.
What also allows to set an instance variable in this controller:
RSpec.describe TestController, type: :controller do
describe "#foo" do
it "should be passed" do
#specific_user = FactoryGirl.create(:user)
allow(controller).to receive(:foo!).and_return(true)
controller.instance_variable_set(:#user, #specific_user)
end
end
end

Related

Undefined method response when using let and subject in RSpec

I tried to write some tests for the "show" action in Rails API
require 'rails_helper'
RSpec.describe AirlinesController, type: :controller do
describe "GET #show" do
before(:each) do
#airline = FactoryGirl.create(:airline)
get :show, id: #airline.id
end
it "should return the airline information" do
airline_response = json_response
expect(airline_response[:name]).to eql #airline.name
end
it {should respond_with :ok}
end
end
The test passed. However, when I try to use let and subject like this
require 'rails_helper'
RSpec.describe AirlinesController, type: :controller do
describe "GET #show" do
let(:airline) {FactoryGirl.create(:airline)}
subject {airline}
before(:each) do
get :show, id: airline.id
end
it "should return the airline information" do
airline_response = json_response
expect(airline_response[:name]).to eql airline.name
end
it {should respond_with :ok}
end
end
It showed "NoMethodError undefined method `response' for ..."
This makes me confused!
Don't set the subject. The subject of a controller spec is the controller, not a model object. Just remove the line that sets subject and you shouldn't get that error any more.
it {should respond_with :ok}
I assume this line takes the subject and makes a response call.
The recommended syntax is:
it "returns 200" do
expect(response).to be_success
end
Or maybe your json_response helper method is using subject.response instead of response.

Testing a before filter in rails controller

I seem to be stuck. I am trying to shore up some rspec testing and want to make sure the the correct before_filter methods are getting called for controllers. However, I am getting feedback saying the method never gets called.
The error:
Failure/Error: expect(controller).to receive(:authorize)
(#<UsersController:0x007fca2fd27110>).authorize(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
The spec:
require "rails_helper"
RSpec.describe UsersController, :type => :controller do
let(:school){ FactoryGirl.create :school }
let(:user){ FactoryGirl.create :teacher}
before(:each){
allow(controller).to receive(:current_user).and_return(user)
school.teachers << user
}
context "Get #show" do
before(:each){ get :show, school_id: school.id, id: user.id }
it "responds successfully with an HTTP 200 status code" do
expect(controller).to receive(:authorize)
expect(response).to have_http_status(200)
end
it "renders the show template" do
expect(response).to render_template("show")
end
end
end
The controller:
class UsersController < ApplicationController
before_filter :authorize
def show
#user = User.find_by_id params[:id]
#school = #user.school
#coordinators = #school.coordinators
#teachers = #school.teachers
#speducators = #school.speducators
#students = #school.students
end
end
Manual testing shows that before is being called, and when I put a p in the authorize method it is called when I run the test, any thoughts on where the test is going wrong?
You must set method expectation before actual call, so your test should look like:
context "Get #show" do
subject { get :show, school_id: school.id, id: user.id }
it "calls +authorize+ befor action" do
expect(controller).to receive(:authorize)
subject
end
end
Check the documentation https://github.com/rspec/rspec-mocks#message-expectations

index rspec test failing

Im having difficulties with writing a spec for an index action of a controller im trying to test. The controller looks like this:
class MyGamesResultsController < ApplicationController
def index
#contest = Contest.find(params[:contest_id])
#my_entry = current_user.entries.where(contest_id: params[:contest_id])
#points_per_player = #my_entry[0].points_per_player
#total_points = #my_entry[0].total_points
end
end
and my spec looks like this:
require 'rails_helper'
RSpec.describe MyGamesResultsController, type: :controller do
describe 'GET /index' do
let!(:user) { create(:user) }
before :each do
sign_in user
get :index
end
it 'renders the index template' do
expect(subject).to render_template(:index)
end
end
end
The error that the spec returns says this:
Failure/Error: get :index
ActiveRecord::RecordNotFound:
Couldn't find Contest with 'id'=
Can anyone figure out what is wrong?
Solved it! had to do this:
require 'rails_helper'
RSpec.describe MyGamesResultsController, type: :controller do
describe "GET index" do
let!(:user) { create(:user) }
let!(:contest) { create(:contest) }
let!(:my_entry) { create(:entry, user_id: user.id, contest_id: contest.id) }
before :each do
sign_in user
get :index, contest_id: contest.id
end
it "renders the index template" do
(expect (response.status)).to eql(200)
end
end
end

rspec controller subject instance_variables

I have a controller and tests it through rspec:
describe "GET 'index'" do
subject { get :index }
it { expect(subject).to render_template(:index) }
My controller generates instance variables passed to views, smth. like that:
#specifications = current_user.specifications
How can I test that controller pass instance variables correct?
Something like that:
it { expect(subject).assign(:contractors).to match_array(my_array) }
You can use controller helper test method
describe TetsController do
let(:user) { build_stubbed :user }
before do
controller.stub authenticate_user!: true,
current_user: user
end
describe 'GET index' do
let(:plans) { double :plans }
before do
expect(Plan).to receive(:all).and_return(plans)
end
it 'response success' do
get :index
expect(response).to be_success
end
it 'assign plans' do
get :index
expect(assigns(:plans)).to eq plans
end
end
end
Small example. controller has instance variable #plans. It's accessed as assigns(:plans)

Generating RSpec Examples via Functions

I'm trying to add a function to allow for quick testing of redirects for unauthenticated users. Here's what I have so far:
def unauthenticated_redirects_to redirect_path #yeild
context "when not signed in" do
it "redirects to #{redirect_path}" do
yield
expect(response).to redirect_to redirect_path
end
end
end
describe SomeController do
describe 'GET #show' do
unauthenticated_redirects_to('/some_path') { get :show }
context "when signed in" do
# One thing...
# Another thing...
end
end
describe 'GET #whatever' do
unauthenticated_redirects_to('/some_other_path') { get :whatever }
end
end
This doesn't work, however, since the scope and context of the primary describe block is not available to the block passed to unauthenticated_redirects_to. This reasonably leads to the error: undefined method `get' for RSpec::Core::ExampleGroup::Nested_1::Nested_2:Class.
Is there a way around this or is there a cleaner way to accomplish something similar which I should consider?
Here's an approach using shared examples which triggers the example based on shared metadata (:auth => true in this case) and which parses the example group description to pick up some key parameters.
require 'spec_helper'
class SomeController < ApplicationController
end
describe SomeController, type: :controller do
shared_examples_for :auth => true do
it "redirects when not signed in" do
metadata = example.metadata
description = metadata[:example_group][:description_args][0]
redirect_path = metadata[:failure_redirect]
http_verb = description.split[0].downcase.to_s
controller_method = description.match(/#(.*)$/)[1]
send(http_verb, controller_method)
expect(response).to redirect_to redirect_path
end
end
describe 'GET #show', :auth => true, :failure_redirect => '/some_path' do
context "when signed in" do
# One thing...
# Another thing...
end
end
describe 'GET #whatever', :auth => true, :failure_redirect => '/some_other_path' do
end
end
For completeness, here's another shared examples approach, this time using a block parameter with a before call which avoids the original scope problem:
require 'spec_helper'
class SomeController < ApplicationController
end
describe SomeController, type: :controller do
shared_examples_for 'auth ops' do
it "redirects when not signed in" do
expect(response).to redirect_to redirect_path
end
end
describe 'GET #show' do
it_behaves_like 'auth ops' do
let(:redirect_path) {'/some_path'}
before {get :show}
end
context "when signed in" do
# One thing...
# Another thing...
end
end
describe 'GET #new' do
it_behaves_like 'auth ops' do
let(:redirect_path) {'/some_other_path'}
before {get :whatever}
end
end
end
Have a look at rspec shared example.
Using shared_examples_for seemed like overkill given that I was only concerned with a single example. Furthermore, it_behaves_like("unauthenticated redirects to", '/some_other_path', Proc.new{ get :whatever}) seems unnecessarily verbose. The trick is to use #send() to maintain the proper scope.
def unauthenticated_redirects_to path, method_action
context "when not signed in" do
it "redirects to #{path} for #{method_action}" do
send(method_action.first[0], method_action.first[1])
expect(response).to redirect_to path
end
end
end
describe 'GET #new' do
unauthenticated_redirects_to '/path', :get => :new
end

Resources