I am receiving the following errors for my Rspec test in rails
1) MoviesController searching for similar movies should find the similar movies by director
Failure/Error: get :similar, {:id => "1"}
ActiveRecord::RecordNotFound:
Couldn't find Movie with id=1
# ./app/controllers/movies_controller.rb:63:in `similar'
# ./spec/controllers/movies_controller_spec.rb:16:in `block (3 levels) in <top (required)>'
2) MoviesController searching for similar movies should select the Similiar Movies template for rendering
Failure/Error: get :similar, {:id => "1"}
ActiveRecord::RecordNotFound:
Couldn't find Movie with id=1
# ./app/controllers/movies_controller.rb:63:in `similar'
# ./spec/controllers/movies_controller_spec.rb:22:in `block (3 levels) in <top (required)>'
3) MoviesController searching for similar movies it should make the results available to the template
Failure/Error: get :similar, {:id => "1"}
ActiveRecord::RecordNotFound:
Couldn't find Movie with id=1
# ./app/controllers/movies_controller.rb:63:in `similar'
# ./spec/controllers/movies_controller_spec.rb:29:in `block (3 levels) in <top (required)>'
And this is my controller method that is causing it to fail:
def similar
#movie = Movie.find(params[:id])
#movies = Movie.find_all_by_director(Movie.find_by_id(params[:id])[:director])
if #movies.count <= 1
redirect_to movies_path
flash[:notice] = "'#{#movie.title}' has no director info"
end
I can't understand why this test keeps failing with the same error. Any help would be much appreciated
Below are the tests
describe MoviesController do
describe 'searching for similar movies' do
before :each do
#fake_movies = [mock('Movie'), mock('Movie')]
#fake_movie = FactoryGirl.build(:movie, :id => "1", :title => "Star Wars", :director => "George Lucas")
end
it 'should follow the route to the similar movies by director page' do
assert_routing('movies/1/similar', {:controller => 'movies', :action => 'similar', :id => '1'})
end
it 'should find the similar movies by director' do
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_all_by_director).with(#fake_movie.director).and_return(#fake_movies)
get :similar, {:id => "1"}
end
it 'should select the Similiar Movies template for rendering' do
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_all_by_director).with(#fake_movie.director).and_return(#fake_movies)
get :similar, {:id => "1"}
response.should render_template('similar')
end
it 'it should make the results available to the template' do
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_all_by_director).with(#fake_movie.director).and_return(#fake_movies)
get :similar, {:id => "1"}
assigns(:movies).should == #fake_results
end
end
end
FactoryGirl is setup as follows:
FactoryGirl.define do
factory :movie do
title 'Star Wars'
director 'George Lucas'
end
end
The spec calls Factory.build. That does not persist the object to the database. You'll want to use Factory.create to allow Movie.find to work.
Related
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
I just upgraded my app from Rails 3.2 to Rails 4.
I'm working through the various changes that need to be made, but this one is stumping me.
My destroy actions seems to be working in the app, but when I run my rspec tests, they are failing.
This is the method in my controller that seems to be throwing the error (when I comment it out, the tests run without issue).
def undo_link
view_context.link_to("Undo archive.", revert_version_path(#answer.versions.scoped.last), :method => :post)
end
The line is taken from Ryan Bates' Undo with PaperTrail Railscast. The link that it generates is seems working perfectly in the app.
Here are the rspec tests:
describe 'DELETE #destroy' do
it 'deletes the answer from the database' do
expect{ delete :destroy, :id => #answer1, :question_id => #question1 }.to change(Answer, :count).by(-1)
end
it 'redirects to questionss#show' do
delete :destroy, :id => #answer1, :question_id => #question1
expect(response).to redirect_to #question1
end
end
And here is the rspec error that is thrown:
1) AnswersController Admin access DELETE #destroy deletes the answer
from the database
Failure/Error: expect{ delete :destroy, :id => #answer1,
:question_id => #question1 }.to change(Answer, :count).by(-1)
ActionController::UrlGenerationError:
No route matches {:id=>nil} missing required keys: [:id]
# ./app/controllers/answers_controller.rb:87:in `undo_link'
# ./app/controllers/answers_controller.rb:69:in `block (2 levels) in
destroy'
# ./app/controllers/answers_controller.rb:68:in `destroy'
# ./spec/controllers/answers_controller_spec.rb:179:in `block (5
levels) in '
# ./spec/controllers/answers_controller_spec.rb:179:in `block (4
levels) in '
2) AnswersController Admin access DELETE #destroy redirects to
questionss#show
Failure/Error: delete :destroy, :id => #answer1, :question_id =>
#question1
ActionController::UrlGenerationError:
No route matches {:id=>nil} missing required keys: [:id]
# ./app/controllers/answers_controller.rb:87:in `undo_link'
# ./app/controllers/answers_controller.rb:69:in `block (2 levels) in destroy'
# ./app/controllers/answers_controller.rb:68:in `destroy'
# ./spec/controllers/answers_controller_spec.rb:182:in `block (4 levels) in <top (required)>'
Can anyone explain why this is happening and what I can do about it?
Edit - 12/9/13
I raised an error in the method using the better_errors gem in order to attempt reproduce the nil, as Mikhail suggested in the comments. I never got the nil in the app itself - it seems to only show up in rspec testing. Here are the results I got:
>> #answer.versions.scoped.last
=> #<PaperTrail::Version id: 64, item_type: "Answer", item_id: 8, event: "destroy", whodunnit: "6", object: "---\nid: 8\nbody: <p>Shoreditch enim retro, disrupt s...", created_at: "2013-12-09 16:35:08">
>> revert_version_path(#answer.versions.scoped.last)
=> "/versions/64/revert"
>> view_context.link_to("Undo archive.", revert_version_path(#answer.versions.scoped.last), :method => :post)
=> "<a data-method=\"post\" href=\"/versions/64/revert\" rel=\"nofollow\">Undo archive.</a>"
Edit - 12/10/13
Here is the rspec code that sets #answer1:
before :each do
#question1 = create(:question, :id => 99)
#answer1 = create(:answer, :question_id => 99)
sign_in_user(:admin)
end
And the factory:
FactoryGirl.define do
factory :answer do
body { Faker::Lorem.characters(150) }
user_id { 1 }
question_id { 1 }
end
end
I'm running these rspec tests for my controller:
require 'spec_helper'
describe MoviesController do
describe 'searching for similar movies' do
before :each do
#fake_movies = [mock('Movie'), mock('Movie')]
#fake_movie = FactoryGirl.build(:movie, :id => "1", :title => "Star Wars", :director => "George Lucas")
end
it 'should follow the route to the similar movies by director page' do
assert_routing('movies/1/similar', {:controller => 'movies', :action => 'similar', :id => '1'})
end
it 'should find the similar movies by director' do
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_by_director).with(#fake_movie.director).and_return(#fake_movies)
get :similar, {:id => "1"}
end
it 'should select the Similiar Movies template for rendering' do
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_by_director).with(#fake_movie.director).and_return(#fake_movies)
get :similar, {:id => "1"}
response.should render_template('similar')
end
it 'it should make the results available to the template' do
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_by_director).with(#fake_movie.director).and_return(#fake_movies)
get :similar, {:id => "1"}
assigns(:movies).should == #fake_results
end
end
end
Buy they are failing with this output:
Failures:
1) MoviesController searching for similar movies should find the similar movies by director
Failure/Error: get :similar, {:id => "1"}
<Movie(id: integer, title: string, rating: string, description: text, release_date: datetime, created_at: datetime, updated_at: datetime, director: string) (class)> received :find_by_director with unexpected arguments
expected: ("George Lucas")
got: ()
# ./app/controllers/movies_controller.rb:62:in `similar'
# ./spec/controllers/movies_controller_spec.rb:17:in `block (3 levels) in <top (required)>'
2) MoviesController searching for similar movies should select the Similiar Movies template for rendering
Failure/Error: get :similar, {:id => "1"}
<Movie(id: integer, title: string, rating: string, description: text, release_date: datetime, created_at: datetime, updated_at: datetime, director: string) (class)> received :find_by_director with unexpected arguments
expected: ("George Lucas")
got: ()
# ./app/controllers/movies_controller.rb:62:in `similar'
# ./spec/controllers/movies_controller_spec.rb:23:in `block (3 levels) in <top (required)>'
3) MoviesController searching for similar movies it should make the results available to the template
Failure/Error: get :similar, {:id => "1"}
<Movie(id: integer, title: string, rating: string, description: text, release_date: datetime, created_at: datetime, updated_at: datetime, director: string) (class)> received :find_by_director with unexpected arguments
expected: ("George Lucas")
got: ()
# ./app/controllers/movies_controller.rb:62:in `similar'
# ./spec/controllers/movies_controller_spec.rb:30:in `block (3 levels) in <top (required)>'
Finished in 0.15517 seconds
4 examples, 3 failures
Failed examples:
rspec ./spec/controllers/movies_controller_spec.rb:14 # MoviesController searching for similar movies should find the similar movies by director
rspec ./spec/controllers/movies_controller_spec.rb:20 # MoviesController searching for similar movies should select the Similiar Movies template for rendering
rspec ./spec/controllers/movies_controller_spec.rb:27 # MoviesController searching for similar movies it should make the results available to the template
When this is my controller method:
def similar
#movies = Movie.find_by_director(Movie.find_by_id(params[:id]))
end
I do not understand why these tests are failing.
The problem was I was calling the wrong method. find_by_director isn't the method that was supposed to be used, but find_all_by_director, hence what was going to the method was wrong.
You need to first give the request with proper method and then test for the expected result.
Place the below line in the beginning of the failed examples.
get :similar, {:id => "1"}
Like below.
it 'should find the similar movies by director' do
get :similar, {:id => "1"}
Movie.should_receive(:find_by_id).with("1").and_return(#fake_movie)
Movie.should_receive(:find_by_director).with(#fake_movie.director).and_return(#fake_movies)
end
I suggest you to get rid of mocking and work with "real" objects that persist in DB:
describe MoviesController do
describe 'searching for similar movies' do
before do
#movie = FactoryGirl.create(:movie,
:title => "Star Wars",
:director => "George Lucas")
#another_lucas_movie = FactoryGirl.create(:movie,
:title => "Indiana Jones and the Last Crusade",
:director => "George Lucas")
end
it 'should find the similar movies by director' do
get :similar, {:id => #movie.id}
assigns(:movies).should include #another_lucas_movie
end
end
end
See RSpec docs for more details.
Use FactoryGirl.create instead of FactoryGirl.build
.build acts as Model.new You have to save your test records to database.
Have 2 models: lease_booking and lease_log. A lease_booking has_many lease_log and, a lease_log belongs_to a lease_booking. In routes.rb it is:
resources :lease_bookings do
resources :lease_logs
end
There is an error for rspec 'show':
6) LeaseLogsController show should display log content
Failure/Error: get 'show', :lease_booking_id => booking.id, :id => log.id
ActionView::Template::Error:
undefined method `name' for nil:NilClass
# ./app/views/lease_logs/show.html.erb:23:in `_app_views_lease_logs_show_html_erb___751480249_50095404'
# ./spec/controllers/lease_logs_controller_spec.rb:87:in `block (3 levels) in <top (required)>'
The rspec 'show' code is:
it "should display log content" do
booking = Factory(:lease_booking)
log = Factory(:lease_log, :lease_booking_id => booking.id)
get 'show', :lease_booking_id => booking.id, :id => log.id
response.should be_success
end
The controller code is:
def show
#lease_booking = LeaseBooking.find(params[:lease_booking_id])
#lease_log = #lease_booking.lease_log.find(params[:id])
end
Any idea about the error? Thanks.
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)