Testing controllers with local variables using Rspec - ruby-on-rails

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)>'

Related

POST :create rspec controller testing error, wrong number of arguments 2 for 0

I'm trying to test my Post_comments#create action in the controller specs with rspec and I keep getting this error message:
Failure/Error: post :create, :post_id => post.to_param, :post_comment => attributes_for(:post_comment, comment: "New")
ArgumentError:
wrong number of arguments (2 for 0)
# ./spec/controllers/post_comments_controller_spec.rb:95:in `block (4 levels) in <top (required)>'
My Post Comments controller:
class PostCommentsController < ApplicationController
before_action :find_todo_list
def index
#post_comment = #post.post_comments.all
end
def show
#post_comment = #post.post_comments.find(params[:id])
end
def new
#post_comment = #post.post_comments.new
end
def edit
#post_comment = #post.post_comments.find(params[:id])
end
def create
#post_comment = #post.post_comments.new(post_comment_params)
if
#post_comment.save
redirect_to post_post_comments_path
flash[:success] = "Comment added successfully!"
else
flash.now[:error] = "Comment could not be saved"
render 'new'
end
end
def update
#post_comment = #post.post_comments.find(params[:id])
if
#post_comment.update(post_comment_params)
redirect_to post_post_comment_path
flash[:success] = "Comment successfully updated"
else
flash.now[:error] = "Comment could not be updated"
render 'edit'
end
end
def destroy
#post_comment = #post.post_comments.find(params[:id])
#post_comment.destroy
redirect_to post_post_comments_path
flash[:success] = "The comment was successfully deleted"
end
end
private
def find_todo_list
#post = Post.find_by(params[:post_id])
end
def post_comment_params
params.require(:post_comment).permit(:comment)
end
My controller spec that keeps failing:
describe "POST #create" do
context "flash messages" do
let(:post) {create(:post)}
it "sets flash success" do
post :create, :post_id => post.to_param, :post_comment => attributes_for(:post_comment, comment: "New")
expect(flash[:success]).to eq("Comment added successfully!")
end
end
end
I'm using factory girl so here is my factory for post comments, which has a belongs_to association with a post...duh
factory :post_comment do
comment "Post comment"
post
end
Any help would really help me out, thanks!
let(:post) {create(:post)}
# ...
post :create
let is a fancy way of defining a method on the current RSpec example. post is now a method that accepts 0 arguments, thus the message wrong number of arguments (2 for 0).
Try naming your Post object something else.

Rspec failing on API create action

Rspec keeps failing on this create action, i.e. the event is a Nil object when the test runs. The other tests passes on all of my user and user token authentication secttions fine. It's loading the FFaker/Factory-Girl data properly. Can't seem to find an answer anywhere.
events_controller_rspec.rb
describe "POST #create" do
context "when is successfully created" do
before(:each) do
user = FactoryGirl.create(:user)
#event_attributes = FactoryGirl.attributes_for(:event)
api_authorization_header user.auth_token
post :create, { user_id: user.id, event: #event_attributes }, format: :json
end
it "renders the JSON representation for the event record just created" do
event_response = json_response[:event]
expect(event_response[:name]).to eql #event_attributes[:name]
end
it { should respond_with 201 }
end
events_controller.rb
def create
event = current_user.events.build(event_params)
if event.save
render json: event, status: 201, location: [:api, event]
else
render json: { errors: event.errors }, status: 422
end
end
private
def event_params
params.require(:event).permit(:name, :user_id)
end
end
app/concerns/authenticatable.rb
module Authenticable
# Devise methods overwrites
def current_user
#current_user ||= User.find_by(auth_token: request.headers['Authorization'])
end
end
Rspec Results
Failures:
1) Api::V1::EventsController POST #create when is successfully created renders the JSON representation for the event record just created
Failure/Error: expect(event_response[:name]).to eql #event_attributes[:name]
NoMethodError:
undefined method `[]' for nil:NilClass
# ./spec/controllers/api/v1/events_controller_spec.rb:75:in `block (4 levels) in <top (required)>'
2) Api::V1::EventsController POST #create when is successfully created should respond with 201
Failure/Error: it { should respond_with 201 }
Expected response to be a 201, but was 422
# ./spec/controllers/api/v1/events_controller_spec.rb:78:in `block (4 levels) in <top (required)>'
Your event_params method tells in params.require(:event).permit(:name, :user_id) that the user_id needs to be part of the event attributes.
Change:
post :create, { user_id: user.id, event: #event_attributes }, format: :json
to
post :create, { event: #event_attributes.merge(user_id: user.id) }, format: :json

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

Rspec: the proper way to use params in a get method

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.

wrong mock_model in rspec

Here is the rspec code for create in controller:
describe "'create' successful" do
before(:each) do
#customer = mock_model(Customer)
#customer.stub(:save).and_return(true)
session[:sales] = true
session[:user_id] = 1
session[:user_name] = "sales.name"
session[:page_step] = 1
session['page1'] = customers_path
end
it "should create one customer record" do
lambda do
put 'create', #customer
end.should change(Customer, :count).by(1)
end
it "should redirect to customers path" do
put 'create', #customer
flash[:notice].should_not be_nil
response.should redirect_to(customers_path)
end
end
Here is the create in controller:
def create
if session[:sales]
#customer = Customer.new(params[:customer], :as => :roles_new_update)
#customer.sales_id = session[:user_id]
if #customer.save
#message = "New customer #{params[:name]} was created. Please check it out"
#subject = "New customer #{params[:name]} was created BY {#session[:user_name]}"
UserMailer.notify_tl_dh_ch_ceo(#message, #subject, session[:user_id])
redirect_to session[('page' + session[:page_step].to_s).to_sym], :notice => 'Customer was created successfaully!'
else
render 'new', :notice => 'Customer was not saved!'
end
end
end
Here is the error in rspec:
1) CustomersController GET customer page 'create' successful should create one customer record
Failure/Error: put 'create', #customer
NoMethodError:
undefined method `symbolize_keys' for "1001":String
# ./spec/controllers/customers_controller_spec.rb:40:in `block (5 levels) in <top (required)>'
# ./spec/controllers/customers_controller_spec.rb:39:in `block (4 levels) in <top (required)>'
2) CustomersController GET customer page 'create' successful should redirect to customers path
Failure/Error: put 'create', #customer
NoMethodError:
undefined method `symbolize_keys' for "1002":String
# ./spec/controllers/customers_controller_spec.rb:45:i
The problem is with the mocking data of #customer. What's wrong with the mocking in spec?
Thanks.
There is nothing wrong, you simply have to make your model fetch the mock you've created.
Proceed this way:
#customer = mock_model(Customer)
#customer.stub(:save).and_return(true)
Customer.should_receive(:new).and_return(#customer)
Rereading your question I figured out that Rails could be complaining because when you pass a variable to your route, it should respond to to_param.
But your mock doesn't: it's nothing.
I'd say choose among the following:
use a Factory which is a real ActiveRecord object with the right properties
edit your test for something like put 'create', 1234 or any integer you want.
BTW, put triggers the update action which is not supposed to created objects but simply change them.

Resources