Rspec: the proper way to use params in a get method - ruby-on-rails

This is my RSpec test:
describe "GET #show" do
it "assigns the requested user to #user" do
user = FactoryGirl.build(:user)
get :show, id: user
assigns(:user).should eq(user)
end
end
This is my rails controller:
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
I get the following error message:
Failures:
1) UsersController GET #show assigns the requested user to #user
Failure/Error: get :show, id: user
ActiveRecord::RecordNotFound:
Couldn't find User without an ID
# /home/tim/fairym/app/controllers/users_controller.rb:25:in `show'
# ./users_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
What is the proper way to use the get method here in order to make the test pass?

You should use FactoryGirl.create and not build. The reason is that create actually makes an entry in the database, including ID. build only makes an object in the memory, without an ID.

Related

Rails Rspec - undefined method `session'

Im testing my Session Controller but Im getting this error, the log in feature works, I tested it on the browser but Im new to testing on Rspec and can't get this to work
Failure/Error: expect(response.session[:user_id]).to eq(#user_attr.id)
NoMethodError:
undefined method `session' for #<ActionController::TestResponse:0xd30df10>
# ./spec/controllers/sessions_controller_spec.rb:20:in `block (3 levels) in <top (required)>'
This is the code of my controller:
def new
#user = User.new
end
def create
#user = User.find_by(username: params[:user][:username])
if #user && #user.authenticate(params[:user][:password])
session[:user_id] = #user.id
redirect_to root_path
else
render :new
end
end
Rspec code:
require 'rails_helper'
RSpec.describe SessionsController, type: :controller do
describe "get Login page" do
it "returns http status" do
get :new
expect(response).to have_http_status(:success)
end
end
describe "session" do
before(:each) do
#user = FactoryGirl.create(:user)
#user_attr = FactoryGirl.attributes_for(:user)
end
it "gives session" do
request.session[:user_id] = nil
post :create, user: #user_attr
expect(response.session[:user_id]).to eq(#user_attr.id)
end
end
end
session is a variable that is available without the request/response context as shown in your example. If you want to manipulate it or check the values it contains, you can simply do something like this:
it "gives session" do
session[:user_id] = nil
post :create, user: #user_attr
expect(session[:user_id]).to eq(#user_attr.id)
end

Can't get my rspec test to pass

I have a test I'm trying to pass but I can't get it to pass, I am new to this.
Here's a link to my git repository https://github.com/FrankWiebe/nameofapp
The failure message comes up like so:
1) UsersController GET #show No user is logged in redirects to login
Failure/Error: expect(response).to redirect_to(root_path)
Expected response to be a <redirect>, but was <200>
# ./spec/controllers/users_controller_spec.rb:24:in `block (4 levels) in <top (required)>'
This is the code:
require 'rails_helper'
describe UsersController, :type => :controller do
before do
#user = User.create(email: "doe#example.com", password: "1234567890")
end
describe "GET #show" do
context "User is logged in" do
before do
sign_in #user
end
it "loads correct user details" do
get :show, id: #user.id
expect(response).to have_http_status(200)
expect(assigns(:user)).to eq #user
end
end
context "No user is logged in" do
it "redirects to login" do
get :show, id: #user.id
expect(response).to redirect_to(root_path)
end
end
end
end
Perhaps #user is carried on as being logged in from one test to the next, so the 2nd show succeeds. Try to sign off the user whithin the first test.
As this is not the case, check your config/routes.rb.
Resources requiring login should be wrapped within a do...end
authenticate(:user) do
resources :user
...other stuff
end

Controller Spec #Show Action: exoected: [Object], got: nil Error - RSpec 3

I am going through Aaron Sumner's Everyday Rails Testing with RSpec (3) book, and can't get passed the following step:
I am trying to spec out a users#show action with:
users_controller_spec:
...
describe 'GET #show' do
it "assigns the requested contact to #user" do
user = create(:user)
get :show, id: user.id
expect(assigns(:user)).to eq user
end
it "renders the :show template" do
user = create(:user)
get :show, id: user.id
expect(response).to render_template :show
end
end
...
users_controller:
class UsersController < ApplicationController
before_action :authenticate
before_action :set_user, only: [:show]
### Read:
def show
...
end
private
def set_user
#user = User.find(params[:id])
end
end
application_controller helpers:
def authenticate
redirect_to home_path, alert: 'Please log in first' if current_user.nil?
end
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
Errors:
Failures:
1) UsersController GET #show assigns the requested user to #user
Failure/Error: expect(assigns(:user)).to eq user
expected: #<User id: 1, ...">
got: nil
(compared using ==)
# ./spec/controllers/users_controller_spec.rb:10:in `block (3 levels) in <top (required)>'
2) UsersController GET #show renders the :show template
Failure/Error: expect(response).to render_template :show
expecting <"show"> but rendering with <[]>
# ./spec/controllers/users_controller_spec.rb:17:in `block (3 levels) in <top (required)>'
Your issue comes from before_action :authenticate either you need to set yourself as authenticated in the specs or you need to skip authentication for the show action.
You would normally test like:
context 'when logged in' do
before(:each) do
set_as_logged_in
end
# your specs
end
where the set_as_logged_in method would differ based on the way you are authenticating. Then you could also have a 'when not logged in' context to verify the action behaves as expected when you are not logged in.

Rspec user controller - NoMethodError: undefined method 'user_url'

I'm now making Rspec test for users_controller.rb. However I'm in trouble the error NoMethodError: undefined method 'user_url' as follow.
FF
Failures:
1) UsersController PUT update user update does not succeed
Failure/Error: put :update, {:id => user.to_param}, valid_session, :user_route => user
NoMethodError:
undefined method `user_url' for #<UsersController:0x52e40e0>
# ./app/controllers/users_controller.rb:21:in `block (2 levels) in update'
# ./app/controllers/users_controller.rb:18:in `update'
# ./spec/controllers/users_controller_spec.rb:64:in `block (3 levels) in <top (required)>'
2) UsersController PUT update user update succeeds
Failure/Error: put :update, {:id => user.to_param}, valid_session, :user_route => user
NoMethodError:
undefined method `user_url' for #<UsersController:0x53bc560>
# ./app/controllers/users_controller.rb:21:in `block (2 levels) in update'
# ./app/controllers/users_controller.rb:18:in `update'
# ./spec/controllers/users_controller_spec.rb:58:in `block (3 levels) in <top (required)>'
Finished in 0.679 seconds
2 examples, 2 failures
Failed examples:
rspec ./spec/controllers/users_controller_spec.rb:61 # UsersController PUT update user update does not succeed
rspec ./spec/controllers/users_controller_spec.rb:56 # UsersController PUT update user update succeeds
Randomized with seed 33412
users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "user#edit" }
format.json { render json: #idea.errors, status: :unprocessable_entity }
end
end
end
end
Also here is my Rspec users_controller_spec.rb. I made two tests about "POST update". One is for being updated successfully. Another is for not being updated. (About the latter, I put the stub User.stub(:update_attribute).and_return(false) which I expect that "update_attribute" returns "false" so that process proceeds to "else".)
require 'spec_helper'
describe UsersController do
let(:valid_attributes) { {
"email" => "hoge#hogehoge.com",
"password" => "12345678"
} }
def valid_session
{}
end
describe "PUT update" do
it "user update succeeds" do
user = User.create! valid_attributes
put :update, {:id => user.to_param}, valid_session
assigns(:user).should eq(user)
end
it "user update does not succeed" do
user = User.create! valid_attributes
User.stub(:update_attribute).and_return(false)
put :update, {:id => user.to_param}, valid_session
assigns(:user).should eq(user)
response.should render_template("edit")
end
end
end
I have no idea to solve this, because I cannot understand where user_url did come. So I would like to have your help.
When you use redirect_to #user, rails sends that request to UsersController#show, but it does so by calling user_url(#user). If I had to guess, you probably don't have the line that defines user_url:
resources :users
in your routes.rb file. This would automatically create the named route user_url that your controller is referencing with redirect_to #user
Alternatively, you could define the route yourself in your routes.rb file like so:
get "/users/show" => "users#show", as: :user
But that's not really the 'Rails-y' way to do it. At any time, you can run the command rake routes in the terminal to see all the named routes you have defined in your routes.rb file. If user isn't there, then you need to define it like I mentioned above.
More info on named routes here: http://guides.rubyonrails.org/routing.html#singular-resources
If you are using devise then check if the following method returns anything.
def after_sign_in_path_for(resource)
in application_controller.rb
If the method returns nothing you will receive the error:
undefined method `user_url' for #
I also ended up removing
stored_location_for(resource)
in after_sign_in_path_for(resource) because it was causing an endless loop. Refer to this answer for details.
rails:3 Devise signup Filter chain halted as :require_no_authentication rendered or redirected

Testing controllers with local variables using Rspec

I'm new to Rspec and fairly new to RoR. With that said, I've exhausted all my options trying to get this to work. I have a variable in a method to create a User in the create action of my UserController.rb. This variable gets the data from an authentication method. Then, I use this local variable, which is a response from an API call, to create the user according to the variables parameters. I've tried everything to my knowledge of Rspec, which is not much, without luck. I keep getting errors because the data variable is nil since I stub/mock the method and the variable.
If anyone could help me figure out how to test this or link me to a good tutorial (I've read a bunch) on how to do this, I would really appretiate it.
Here's my code:
users_controller.rb
def get_google_data
...
data = response.parsed #OAuth2
#id = data['id']
#email = data['email']
#fname = data['given_name']
#lname = data['family_name']
end
def create
get_google_data
puts "Got google data"
puts #id
if !#id.nil?
puts "data is not nil"
#user = User.find_by_google_id(#id)
puts #user
if #user.nil?
puts "inside user condition"
#user = User.new(:email => #email, :google_id => #id,
:first_name => #fname,
:last_name => #lname)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
else
puts "ended in the right Place"
render json: #user, location: #user
end
end
end
users_controller_spec.rb
describe "should not create duplicate user" do
it "returns user object that was previously created" do
#user = mock_model(User, :google_id=>1)
#controller.should_receive(:get_google_data).and_return(:true)
controller.instance_variable_set(:#id, 1)
User.stub!(:find_by_google_id).with(1).and_return(#user)
post :create
#user.should_not be_nil
end
end
I'm having 2 problems.
The test fails because I cannot control the data in data['id'] and the following assignments.
Failure/Error: post :create
NoMethodError:
undefined method `[]' for nil:NilClass
# ./app/controllers/users_controller.rb:109:in `get_google_data'
# ./app/controllers/users_controller.rb:118:in `create'
# ./spec/controllers/users_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
The call to return #user gives me a circular reference error:
2) UsersController should not create duplicate user returns user object that was previously created
Failure/Error: post :create
ActiveSupport::JSON::Encoding::CircularReferenceError:
object references itself
# ./app/controllers/users_controller.rb:141:in `create'
# ./spec/controllers/users_controller_spec.rb:24:in `block (3 levels) in <top (required)>'

Resources