How to stub error raising using Rspec in Rails? - ruby-on-rails

I'm new to Rails and Rspec and I'm using Rspec to test this controller method which includes exception handling:
def search_movies_director
#current_movie = Movie.find(params[:id])
begin
#movies = Movie.find_movies_director(params[:id])
rescue Movie::NoDirectorError
flash[:warning] = "#{#current_movie} has no director info"
redirect_to movies_path
end
end
I can't figure out how to correctly test the said path: after invalid search (when error is received) it should redirect to the homepage. I tried something like this:
describe MoviesController do
describe 'Finding Movies With Same Director' do
#some other code
context 'after invalid search' do
it 'should redirect to the homepage' do
Movie.stub(:find)
Movie.stub(:find_movies_director).and_raise(Movie::NoDirectorError)
get :search_movies_director, {:id => '1'}
response.should redirect_to movies_path
end
end
end
end
After running the test fails with an error: NameError: uninitialized constant Movie::NoDirectorError
How to fake raising an error in this test so it actually checks whether redirect happens?
Thanks!
UPDATE:
As nzifnab explained, it couldn't locate Movie::NoDirectorError. I forgot to define this exception class. So I added it to app/models/movie.rb :
class Movie < ActiveRecord::Base
class Movie::NoDirectorError < StandardError ; end
#some model methods
end
This solved my problem and this test passes.

You're very close. You need to add any_instance in there.
Movie.any_instance.stub(:find_movies_director).and_raise(Movie::NoDirectorError)
edit: I misread the post. The above would work given an instance of Movie, but not for OP's question.

The error indicates it doesn't know where that Movie::NoDirectorError exception class is defined. You might need to specifically require the file where that class is defined or the test may not be able to find it.
Rails will automatically attempt to locate any constant missing constants using a conventional file directory format. It will look for a file in the load_path at movie/no_director_error and movie based on the name of the constant. If the file is not found or the file doesn't define the expected constant than you'll need to specifically require the file yourself.

In Rails 4.1:
verse_selector = double("VerseSelector", select_verses: ActiveRecord::RecordNotFound.new("Verses not found"))
verse_selector.select_verses will now return an ActiveRecord::RecordNotFound

Related

Uninitialized constant in model affecting controller

I am working on a test application in which someone who favors something becomes a subscriber of that person.
The controller:
def favorite_subscribe
#favorite_subscription = FavoriteSubscription.add_favorite_subscription(current_user, #user)
#user.favorite_subscriber_total = #user.favorite_subscriber_total + 1
#user.save
redirect_to :back, :notice => "Congratulations, you have favorited #{#user.username}."
end
The model:
def self.add_favorite_subscription(user, favorite_subscribe)
user.favorite_subscriptions.where(:subscribe_id => subscribe.id).first_or_create
end
# Remove the favorite of the user with the other user
def self.remove_favorite_subscription(user, favorite_subscribe)
user.favorite_subscriptions.where(:subscribe_id => subscribe.id).destroy_all
end
# Get the user with the subscription
def favorite_subscribe
User.find(subscribe_id)
end
I get an error that it cannot autoload constant, and that it expects my model to define it. If anybody can help that would be very appreciated.
Error is here, sorry about that:
Unable to autoload constant FavoriteSubscription, expected /home/jakxna360/rails/test/app/models/favorite_subscription.rb to define it
This usually means that Rails is unable to find the file in which some class is defined and therefore autoloading fails. Rails is very strict about its conventions.
In the context of the posted code, I suggest double checking that
the class FavoriteSubscription (singular) is defined in a file named app/models/favorite_subscription.rb (singular), that a database table is named favorite_subscriptions (plural) and that is exists.
the FavoriteSubscriptionsController (plural) is defined in a file named app/controllers/favorite_subscriptions_controller.rb (plural).

Using result of raise_error (capybara)

I'm using capybara to do some web automation.
There are various points in the code where it says things like
raise_error 'Failed as this is a duplicate' if duplicate?
or
raise_error 'Failed to log in' if logged_in? == false
All of this is abstracted to a module and I'd prefer that module to not rely on anything in the models.
What I'm struggling with is how to access that error text when I'm running it, from outside the model.
i.e.
Class Thing
has_many :notes
def do_something
#done = Module::Task.something(self.attribute)
if #done
self.update_attributes(status:'Done')
else
self.notes.new(text: error.text)
end
end
but I can't work out the syntax to get that error text.
Answer: If I understand you correctly then errors that appeared while completing the task
#done = Module::Task.something(self.attribute)
can be accessed via #done.errors.messages
Example: If I have User model where attribute username has 2 validations: presence and format then error messages display like this:
irb(main):019:0* u = User.new
irb(main):022:0* u.save # wont succeed
irb(main):028:0* u.errors.messages
=> {:uid=>["can't be blank", "is invalid"]}
If you want to test error messages with capybara then you can use the syntax like this:
it 'raises jibberishh' do
expect{User.raise_error_method}.to raise_error("jibberishh")
end

Rspec-rails 4 error: ActiveModel::ForbiddenAttributesError

I have an application running in rails 4.1 using mongoid as the orm. I created a model called User which has an attribute email. I am using RSpec for tests. I created the following spec
require 'spec_helper'
describe 'User' do
before(:each) do
#attr = {
user: {
email: "rahul#gmail.com"
}
}
end
it "should create a valid User instance" do
param = ActionController::Parameters.new(#attr)
param.require(:user).permit!
User.create!(param)
end
end
when I run the spec, I get the following error
Failure/Error: User.create!(param)
ActiveModel::ForbiddenAttributesError:
ActiveModel::ForbiddenAttributesError
I know this is related to strong parameters but couldn't figure out what I am doing wrong.
From the fine manual:
require(key)
[...] returns the parameter at the given key [...]
So saying param.require(:user) does nothing at all to param, it merely does an existence check and returns param[:user].
I think you want to say something more like this:
param = ActionController::Parameters.new(#attr)
User.create!(param.require(:user).permit!)
That usage would match the usual:
def some_controller_method
#user = User.create(user_param)
end
def user_param
param.require(:user).permit!
end
usage in controllers.

Action could not be found in Rspec test for nested route

I am trying to get a handle on how nested routes work with Rspec. I have one of these:
class SupportController < ResourceController
# stuff happens
def support_override
customer = Customer.find_by_id(params[:id])
customer.override( params[:override_key] )
redirect_to("support")
end
end
We have a route:
resources :support do
member do
# loads of paths
get 'support_override/:override_key' => 'support#support_override'
end
end
And the route passes a test:
it "should route GET support/1/support_override/ABCDEF to suport#support_override" do
{ get: '/support/1/support_override/ABCDEF'}.should route_to(controller: 'support', action: 'support_override', id: '1', override_key: 'ABCDEF' )
end
However when I try to test the logic in rspec:
describe SupportController do
# various levels of context and FactoryGirl calls
it "can reach override url" do
get :support_override, { :id=> #customer.id, :override_key="123" }
response.should redirect_to("support")
end
end
I get the following response:
Failure/Error: Unable to find matching line from backtrace
AbstractController::ActionNotFound:
The action 'support_override' could not be found for SupportController
I have no doubt that the problem is with my understanding of how rspec works with nested routes, but I can't see any way to figure out what path Rspec is actually seeking and consequently it's hard to know what I need to change and I'm having trouble locating the relevant documentation.
Is there a way to find what path is being created by the test or can anyone offer guidance on how exactly the path creation works in this situation?
Since, you haven't shared the complete SupportController code, I cannot pin-point exact error. BUT there are two possibilities:
You have defined support_override under private/protected by mistake.
You have closed the class SupportController before support_override method definition, by mistake
Your action must always be public so that its accessible.

named route as a parameter for example group in rspec

I am newbie in testing and using RSpec and need some help:
I have shared example group:
shared_examples_for 'request which do something' do |opts = {}|
respond.should redirect_to(opts[:redirect_to])
end
In my spec file:
describe "behavior" do
it_should_behave_like 'request which do something', :redirect_to => root_path
end
Looks great, but I get this error:
Exception encountered: #<NameError: undefined local variable or method `root_path' for #<Class:0x000000069e2838>>
and it points on line with 'it_should_behave_like ... '
I tried to include Rails.application.routes.url_helper in spec_helper, but it doesn't work anyway.
By the way it perfectly works from example like this:
describe "behavior" do
it "should redirect" do
response.should redirect_to(root_path)
end
end
(even without explicit including url_helpers)
Thanks for any help.
You can't use path helpers within example groups, but there is a workaround. See this answer:
Passing a named route to a controller macro in RSpec
With this, you can pass a symbol and use send.
In example groups, write Rails.application.routes.url_helpers.root_path
instead of just root_path. You could create your own helper method to
make repeated calls short.

Resources