I'm using devise + rspec + factory + shoulda and having trouble with my controller specs. I've read a bunch of articles and docs but couldn't figure out what the best way is to log_in the user and use that user instance.
Task is nested under user so index route is /users/:user_id/tasks and task belongs_to :assigner, class_name: "User" and belongs_to :executor, class_name: "User"
At the moment with following code both tests fail. What is the best approach for properly sign_in the user and use it in the controller tests?
The error message for the first one:
Failure/Error: expect(assigns(:tasks)).to eq([assigned_task, executed_task])
expected: [#<Task id: 1, assigner_id: 1, executor_id: 2, .....>, #<Task id: 2, assigner_id: 3, executor_id: 1, ......>]
got: nil
(compared using ==)
The error for the second one:
Failure/Error: it { is_expected.to respond_with :ok }
Expected response to be a 200, but was 302
tasks_controller_spec.rb
require "rails_helper"
describe TasksController do
describe "when user is signed in" do
describe "collections" do
login_user
let(:assigned_task) { create(:task, assigner: #user) }
let(:executed_task) { create(:task, executor: #user) }
let(:other_task) { create(:task) }
context "GET index" do
before do
get :index, user_id: #user.id
end
it "assigns user's tasks" do
expect(assigns(:tasks)).to eq([assigned_task, executed_task])
end
it { is_expected.to respond_with :ok }
end
context "GET incoming_tasks"
end
end
end
controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = create(:user)
sign_in #user
end
end
end
tasks controller
def index
#tasks = Task.alltasks(current_user).uncompleted.includes(:executor, :assigner).order("deadline DESC").paginate(page: params[:page], per_page: Task.pagination_per_page)
end
Add following line in rails_helper.
config.include ControllerMacros, :type => :controller
SEE this thread.
I am assuming this only fails in rspec. When you test in browser it works fine.
Related
I'm following this tutorial for a rails API but it is a little outdated and some things don't seem to work with newer versions of rails. I'm having a hard time with the user controller specs:
user_controller_spec.rb
require 'rails_helper'
RSpec.describe Api::V1::UsersController, type: :controller do
describe "GET #show" do
before(:each) do
#user = FactoryGirl.create :user
get :show, params: {id: #user.id}
end
it "returns the information about a reporter on a hash" do
user_response = JSON.parse(response.body, symbolize_name: true)
expect(user_response[:email]).to eql #user.email
end
it { expect(response).to have_http_status(200) }
end
end
user_controller.rb
class Api::V1::UsersController < ApplicationController
def show
render json: User.find(params[:id])
end
end
user.rb factory
FactoryGirl.define do
factory :user do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
But, this isn't working, the email doesn't seem to match. Any ideas what could be wrong?
Failures:
1) Api::V1::UsersController GET #show returns the information about a reporter on a hash
Failure/Error: expect(user_response[:email]).to eql #user.email
expected: "mitzie_nikolaus#rice.com"
got: nil
(compared using eql?)
# ./spec/controllers/api/v1/users_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
The code is correct, but you've made a typo in using the symbolize_names option for JSON.parse.
I assume, that because you do not copy-paste examples, but type it by your own, which is great, because it's better for learning.
To fix the test just correct this line (change symbolize_name to symbolize_names):
user_response = JSON.parse(response.body, symbolize_names: true)
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
please help solve the problem. i use gem 'devise', gem 'rspec' and this tutorial:
Authentication with Devise in Rspec tests
.i need #user output in console.
emails_controller_spec.rb:
RSpec.describe EmailsController, type: :controller do
let(:valid_attributes) {
{
email: Faker::Internet.email,
description: Faker::Lorem.paragraph(7),
user_id: 1
}
}
describe "GET #index" do
login_user
it "assigns all emails as #emails" do
email = Email.create! valid_attributes
get :index
binding.pry
expect(assigns(:emails)).to eq([email])
#expect(assigns(#user)).to eq(#user)
end
end
end
spec/factories/users.rb:
FactoryGirl.define do
factory :user do
sequence(:email){ |i| "us#{i}#ad.ad" }
password 'qwertyui'
password_confirmation{ |u| u.password }
role :none
end
end
spec/support/controller_macros.rb:
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
end
i user gem 'pry' for see on variables from console after run the test. but i see follow:
kalinin#kalinin ~/rails/mailer $ rspec spec/controllers
From: /home/kalinin/rails/mailer/spec/controllers/emails_controller_spec.rb # line 56 :
[1] pry(#<RSpec::ExampleGroups::EmailsController::GETIndex>)> #user
=> nil
[2] pry(#<RSpec::ExampleGroups::EmailsController::GETIndex>)> user
NameError: undefined local variable or method `user' for #<RSpec::ExampleGroups::EmailsController::GETIndex:0x00000007c4e670>
from /home/kalinin/.rvm/gems/ruby-2.0.0-p598/gems/rspec-expectations-3.3.1/lib/rspec/matchers.rb:966:in `method_missing'
[3] pry(#<RSpec::ExampleGroups::EmailsController::GETIndex>)> puts page.html
=> nil
i need displays user-object or displays html-code of page. please help
I hope you have included your ControllerMacros module somewhere. If not add that to top of your emails_controller_spec.rb file as include ControllerMacros
Add login_user in a before block -
describe "GET #index" do
before { login_user }
it "assigns all emails as #emails" do
email = Email.create! valid_attributes
get :index
binding.pry
expect(assigns(:emails)).to eq([email])
expect(assigns(#user)).to eq(#user)
end
end
Also change your ControllerMacros to:
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = FactoryGirl.create(:user)
sign_in #user
end
end
end
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
I have devise authentication and registration set up on my Rails app. I'm using after_sign_in_path_for() to customise the redirect when the user signs in based on various scenarios.
What I'm asking is how to test this method? It seems hard to isolate since it is called automatically by Devise when the user signes in. I want to do something like this:
describe ApplicationController do
describe "after_sign_in_path_for" do
before :each do
#user = Factory :user
#listing = Factory :listing
sign_in #user
end
describe "with listing_id on the session" do
before :each do
session[:listing_id] = #listing.id
end
describe "and a user in one team" do
it "should save the listing from the session" do
expect {
ApplicationController.new.after_sign_in_path_for(#user)
}.to change(ListingStore, :count).by(1)
end
it "should return the path to the users team page" do
ApplicationController.new.after_sign_in_path_for(#user).should eq team_path(#user.team)
end
end
end
end
end
but that's obviously not the way to do it because I just get an error:
Failure/Error: ApplicationController.new.after_sign_in_path_for(#user)
RuntimeError:
ActionController::Metal#session delegated to #_request.session, but #_request is nil: #<ApplicationController:0x00000104581c68 #_routes=nil, #_action_has_layout=true, #_view_context_class=nil, #_headers={"Content-Type"=>"text/html"}, #_status=200, #_request=nil, #_response=nil>
So, how can I test this method?
Oddly, I was wondering this very thing today. Here's what I came up with. I created an anonymous subclass of ApplicationController. In this anonymous subclass, I exposed the protected methods that I wanted to test as public methods. Then I tested them directly.
describe ApplicationController do
controller do
def after_sign_in_path_for(resource)
super resource
end
end
before (:each) do
#user = FactoryGirl.create(:user)
end
describe "After sigin-in" do
it "redirects to the /jobs page" do
controller.after_sign_in_path_for(#user).should == jobs_path
end
end
end
On a similar note - if you want to test the redirect after sign-up, you have two options.
First, you can follow a pattern similar to above and very directly test the method in RegistrationsController:
require 'spec_helper'
describe RegistrationsController do
controller(RegistrationsController) do
def after_sign_up_path_for(resource)
super resource
end
end
describe "After sign-up" do
it "redirects to the /organizations/new page" do
#user = FactoryGirl.build(:user)
controller.after_sign_up_path_for(#user).should == new_organization_path
end
end
end
Or, you can take a more integration-testing sort of approach and do the following:
require 'spec_helper'
describe RegistrationsController do
describe "After successfully completing the sign-up form" do
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
it "redirects to the new organization page" do
post :create, :user => {"name" => "Test User", "email" => "test#example.com", "password" => "please"}
response.should redirect_to(new_organization_path)
end
end
end
For the newcomers, I would recommend doing this way:
RSpec.describe ApplicationController, type: :controller do
let(:user) { create :user }
describe "After sing-in" do
it "redirects to the /yourpath/ home page" do
expect(subject.after_sign_in_path_for(user)).to eq(yourpath_root_path)
end
end
end
I found this answer through Google recently and thought I would add my solution. I didn't like the accepted answer because it was testing the return value of a method on the application controller vs testing the desired behavior of the app.
I ended up just testing the call to create a new sessions as a request spec.
RSpec.describe "Sessions", type: :request do
it "redirects to the internal home page" do
user = FactoryBot.create(:user, password: 'password 123', password_confirmation: 'password 123')
post user_session_path, params: {user: {email: user.email, password: 'password 123'}}
expect(response).to redirect_to(internal_home_index_path)
end
end
(Rails 5, Devise 4, RSpec 3)
context "without previous page" do
before do
Factory.create(:user, email: "junior#example.com", password: "123456", password_confirmation: "123456")
request.env["devise.mapping"] = Devise.mappings[:user]
post :create, user: { email: "junior#example.com", password: "123456" }
end
end
it { response.should redirect_to(root_path) }
context "with previous page" do
before do
Factory.create(:user, email: "junior#example.com", password: "123456", password_confirmation: "123456")
request.env["devise.mapping"] = Devise.mappings[:user]
request.env['HTTP_REFERER'] = 'http://test.com/restaurants'
post :create, user: { email: "junior#example.com", password: "123456" }
end
it { response.should redirect_to("http://test.com/restaurants") }
end