I want to add a feature that returning all movies info belonging to one director by clicking one button. It has been implemented and works well, but I got some error when writing Rspec tests. The error message is:
(1)MoviesController searching for movies by the same director when 'Director' info exists should post #director and #movies variables for view
Failure/Error: get :find_movies_by_same_director, :movie_id => #movie.id, :movie_director => #movie.director
ActionController::RoutingError:
No route matches {:movie_id=>"1", :movie_director=>["Star Wars", "THX-1138"], :controller=>"movies", :action=>"find_movies_by_same_director"}
# ./spec/controllers/movie_controller_spec.rb:15:in `block (4 levels) in <top (required)>'
Similar errors of no routes found occured for all the following tests, so I think there might be some problems for my routes file. Here is the path I specify in routes.rb:
get '/movies/:id/director', to: 'movies#find_movies_by_same_director', as: 'director'
And the Rspec file is:
require "spec_helper"
describe MoviesController do
it { should respond_to(:find_movies_by_same_director) }
describe "searching for movies by the same director" do
context "when 'Director' info exists" do
before do
#movie = double(:title => "Star Wars", :id => "1", :director => "George Lucas")
Movie.stub(:find).with("1").and_return #movie
#lucas_films = ["Star Wars", "THX-1138"]
#movie.stub(:director).and_return #lucas_films
end
it "should post #director and #movies variables for view" do
get :find_movies_by_same_director, :movie_id => #movie.id
assigns(:director).should == #movie.director
assigns(:movies).should == #lucas_films
end
end
end
The controller method is:
def find_movies_by_same_director
#movie = Movie.find(params[:id])
#director = #movie.director
if (not #director.nil?) and (not #director.empty?)
#movies = Movie.where(director: #director) if (not #director.nil?) and (not #director.empty?);
##movies = Movie.find_by_sql("SELECT * FROM movies i WHERE i.director == '#{#director}'")
render :director
else
flash[:notice] = "'#{#movie.title}' has no director info"
redirect_to root_path
end
I just start to learn Rspec, so any comment or suggestion is appreciated.
As it can be seen in routes, you action expects a parameter :id. You are passing :movie_id instead. So, just replace this line in your spec
get :find_movies_by_same_director, :movie_id => #movie.id
with
get :find_movies_by_same_director, :id => #movie.id
and try again.
As Jagdeep Singh suggests, your action expects an :id parameter, not an :movie_id param. But the line in your spec should pass the :id inside the params.
get :find_movies_by_same_director, params: { id => #movie.id }
I was having the same problem and this answer helped me Controller spec unknown keyword: id
Related
This is the .spec file:
describe MoviesController do
describe 'similar_movies' do
before :each do
#fake_results = [double('movie1'), double('movie2')]
end
it 'should call the model method that searches similar movies' do
expect(Movie).to receive(:similar_movies).with("1").
and_return #fake_results
get :similar_movies, :id => 1
end
it 'should select the similar_movies template for rendering' do
Movie.stub(:similar_movies).and_return(#fake_results)
expect(response).to render_template(:similar_movies)
get :similar_movies, :id => 1
end
# it 'should make the similar_movies results avaliable to that template'do
# end
end
This is the controller action:
# METHOD TO FIND MOVIES WITH THE SAME DIRECTOR AS A GIVEN ONE
def similar_movies
#similar_movies_arr = Movie.similar_movies(params[:id])
#render 'similar_movies'
end
This is the model method:
def self.similar_movies(id)
movie = Movie.find(id)
director = movie['director']
#similar_movies_arr = []
#similar_movies = Movie.where(["director = ?", "#{director}"])
#similar_movies.each do |m|
#similar_movies_arr << m
end
return #similar_movies_arr
end
And this The template:
-#similar_movies_arr.each do |movie|
%h1= movie.title
It's very simple, my first steps with RSpec, but it fails. Please, can someone help me to understand this error
MoviesController similar_movies should select the similar_movies template for rendering
Failure/Error: expect(response).to render_template(:similar_movies)
expecting <"similar_movies"> but rendering with <[]>
The main problem is that you need to do the get before expecting to render template.
get :similar_movies, :id => 1
expect(response).to render_template(:similar_movies)
your test will still have problems because you don't have fake movies to search that you can access the director field. You might want to look at factories to create some test movies.
The third comment is you might want to switch from using the Movie.stub to the new format of allow(Movie).to receive... like the expect earlier just using switching the allow for expect
I am trying to write a controller spec to test that the right partial is rendering after a post request.
Here is the controller method being posted to:
def lookup
#guest = Guest.where("mobile_number = ?", params[:lookup_mobile_phone_number]).first_or_initialize do |g|
g.mobile_number = params[:lookup_mobile_phone_number]
end
if #guest.new_record?
#visit = Visit.new(hotel_id: params[:hotel_id])
render partial: "guests/form"
else
#visit = Visit.new(guest_id: #guest.id, hotel_id: params[:hotel_id])
render partial: "visits/form"
end
end
Here is the spec/controllers/guests_controller_spec.rb I wrote that is failing:
RSpec.describe GuestsController, :type => :controller do
describe "#lookup" do
render_views
let!(:returning_guest) { create(:test_guest) }
context "when guest is already registered with hotel" do
it "renders visits/form" do
post :lookup, :guest => { :lookup_mobile_phone_number => "5553331212"}
expect(response).to render_template(:partial => 'visits/form')
end
end
end
end
Here is the factory I'm using for :test_guest
FactoryGirl.define do
factory :test_guest, :class => 'Guest' do
name 'Jack Guest'
mobile_number '5553331212'
end
end
This is the response I am getting when the test fails
1) GuestsController#lookup when guest is already registered with hotel renders visits/form
Failure/Error: expect(response).to render_template(:partial => 'visits/form')
expecting partial <visits/form> but action rendered <["shared/_hotel_agent_name", "_hotel_agent_name", "guests/_form", "_form"]>.
Expected {"shared/_hotel_agent_name"=>1, "_hotel_agent_name"=>1, "guests/_form"=>1, "_form"=>1} to include "visits/form".
# ./spec/controllers/guests_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
I've been hacking away at this a for a few days now, trying different approaches found on here with no luck. Any help would be much appreciated :)
You send
post :lookup, :guest => { :lookup_mobile_phone_number => "5553331212"}
but in controller, you use
params[:lookup_mobile_phone_number]
not
params[:guest][:lookup_mobile_phone_number]
So to fix it, according to your controller, do
post :lookup, :lookup_mobile_phone_number => "5553331212"
Here is the rspec code for testing show in customers controller:
it "'show' should be successful" do
#category = Factory(:category)
#sales = Factory(:user)
#customer = Factory(:customer, :category1_id => category.id, :sales_id => sales.id)
category = mock_model('Category')
sales = mock_model('User')
customer = mock_model(Category, :sales_id => sales.id, :category1_id => category.id)
get 'show' , :id => customer.id
response.should be_success
end
Here is the error in rspec:
CustomersController GET customer page 'show' should be successful
Failure/Error: get 'show' , :id => customer.id
ActiveRecord::RecordNotFound:
Couldn't find Customer with id=1003
# c:in `find'
# ./app/controllers/customers_controller.rb:59:in `show'
# ./spec/controllers/customers_controller_spec.rb:50:in `block (3 levels) in <top (required)>'
The rspec test passes with the real record created by Factory (see #ed in rspec code)
What's wrong with the mock? Thanks.
The spec is failing inside the controller's action which doesn't know anything about your mocks unless you told it explicitly.
Add this to your spec, before the get statement.
Customer.should_receive(:find).and_return(customer)
I`m trying to test my controller with rspec and always get an error.
users_controller.rb:
def update
#user.update_attributes!(params[:user])
redirect_to #user, :status => 202, :text => render_to_string(:partial => "users/show", :type => "json", :locals => {:user => #user})
#notice, that redirect_to was reinitialized and :text is a parameter for response_body
end
_show.tokamak
user {
id user.id
email user.email
username user.username
}
spec file
it "should NOT update user username" do
username = #user.username
put 'update', :id => #user.id, :user => {:username => username+"abc"}, :format => :json
response.status.should be(202)
response.headers["Location"].length.should be > 0
puts response.body
#user.reload
#user.username.should eq(username)
end
end
So I get an error:
Failure/Error: put 'update', :id =>
#user.id, :user => {:username =>
username+"abc"}, :format => :json
ActionView::Template::Error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
# C:/Users/makaroni4/free_frog/ffapi/app/views/users/_show.tokamak:1:in
_app_views_users__show_tokamak___509498818
_32151168_368311673'
# C:/Users/makaroni4/XXX/XXX/app/controllers/users_controller.rb:22:in
update'
# ./users_controller_spec.rb:34:in
`block (4 levels) in '
So may be I call render_to_string method wrong?
Try stubbing out find?
mock_user = User.stub(:find).with(#user.id) {#user}
To be honest I'd go a few steps further and make sure you mock and stub most of the relevant behavior of the User object (or whatever class #user is). Keep in mind you're only testing that the controller action returns what you expect if you give it valid input--not that the model itself does the right thing.
I had a lot of difficulty wrapping my head around the differences in model vs. controller specs...
I hope this helps...if not, I apologize in advance...
EDIT:
I'll take this a step futher and suggest this test is actually a model test. The actual controller test would be something like as the way your spec test should behave:
it "should NOT update user with invalid input" do
mock_user = mock_model(User, {}).as_null_object
User.stub(:find).with("12") {mock_user}
User.stub(:update_attributes).with({}).and_return(false)
put 'update', :id => "12"
# test that your output is correct, or even if the render target is what you expect.
end
I'm writing tests for my controller. They are very simple, but this error has kept popping up. This is my controller
def show
id=params[:id]
#user=User.find(:first,id)
end
My test
before(:each) do
#user = Fabricate(:user)
sign_in #user
end
...
it "should be successful" do
get "show", :id => #user
response.should be_success
end
And the error message
1) UsersController GET 'show' for the logged in user should be successful
Failure/Error: get "show", :id => #user
TypeError:
can't convert Symbol into Integer
# ./app/controllers/users_controller.rb:6:in `show'
# ./spec/controllers/users_controller_spec.rb:31:in `block (4 levels) in <top (required)>'
your controller is where the mistake is. The find method automatically only returns the first result (it is equivalent in code to User.where(:id => params[:id]).first). Try removing the :first symbol and simply pass in id (User.find(id))
get "show", :id => #user
Your problem here is likely with #user, whose value in the context of your spec is not clear from the example you've posted. You should be passing an integer record id as the value for the params argument to get, for example :id => 1.