mocha stubs with exception - rspec test not passing - ruby-on-rails

Hey,
I am trying to use Mocha and Rspec to test a scenario where a method always raises some exception.
Here is the controller code I am trying to test:
def add_parent
begin
parent = Entity.find_by_id(params[:parent_id])
if !parent.nil?
#entity.add_parent!(parent)
flash[:success] = "Entity successfully updated."
else
raise "Parent does not exist."
end
rescue
flash[:error] = "Something bad happened. #{$!}"
end
redirect_to #entity
end
Here is the test code:
it "should flash error if exception is thrown when adding parent" do
Entity.any_instance.stubs(:add_parent!).raises(Exception)
lambda do
post :add_parent, :id => #entity[:id],
:parent_id => #parent_entity[:id]
end.should_not change(#entity.parents, :count)
flash[:error].should =~ /something bad happened/i
end
Here is the method which is being stubbed:
def add_parent!(parent)
Entity.transaction do
lock!('lock in share mode')
self.parents << parent
end
end
I am getting the following rspec error, which is pretty uninformative so I don't know how to resolve it..
Failures:
1) EntitiesController POST 'add_parent' for signed-in users allow access with edit permission should flash error if exception is thrown when adding parent
Failure/Error: post :add_parent, :id => #entity[:id],
Exception
# ./app/controllers/entities_controller.rb:81:in `add_parent'
# ./spec/controllers/entities_controller_spec.rb:1010

Wooah, first of all, it's a very bad habit to rescue from a big chunk of code without give the exception class you are expecting here. rescueing everything is no good. Best remove the whole rescueing, maybe use find over find_by_id so rails can catch that 404 error and you aren't bothered about. On the other hand, is this error supposed to happen a lot? Looks like some db-stuff going on so I wouldn't expect it to fail.
Secondly, I think you must test like the manual says about raise_error
http://relishapp.com/rspec/rspec-expectations/v/2-6/dir/built-in-matchers/raise-error-matcher

rescue with no argument will rescue StandardError and its descendants. If you want to catch ALL exceptions, you should rescue Exception as all exceptions descend from class Exception.
Source: http://www.ruby-forum.com/topic/44495

Related

Rspec for begin ... rescue block in rails

I am writing a test which should verify that when the object creation saves, indeed the exception is caught.
begin
object.create!(item)
rescue => exception
exception.message
end
rspec
expect{ exception.message }.to eq("Validation failed: Item name can't be blank")
output I got:
Failure/Error: expect{ exception.message }.to eq("Validation failed: Item name can't be blank")
You must pass an argument rather than a block to `expect` to use the provided matcher (eq "Validation failed: Item name can't be blank"), or the matcher must implement `supports_block_expectations?`.
Can anyone help me out?
You cannot test your code like that because the exception variable only exists in the rescue block. Instead, you need to call the method you want to test and add an expectation of what it should return.
Your example actually doesn't have such a method that could be called. I imagine that your method actually looks like this:
def do_stuff_with(object, item)
begin
object.create!(item)
rescue => exception
exception.message
end
end
Then you should be able to have an expectation like this:
expect(do_stuff_with(object, item)).to eq(
"Validation failed: Item name can't be blank"
)

RSpec expect something before expect raise_error

Imagine there's a method that rescues and does some logging.
def do_something
# do stuff
some_client.call(var1)
rescue StandardError => e
# log some stuff.
Rails.logger.error("#{self.class} - Var 1 is #{var1}.") if e.is_a?(MyError)
raise
end
Then in the RSpec, I'd like to
assert the error is raised.
it logs the error
before do
allow(Rails.logger).to receive(:error)
allow(some_client).to receive(:call).and_raise(MyError)
end
it "logs the error" do
subject
expect(Rails.logger).to have_received(:error).with(/some message with var1/)
end
it "raises MyError" do
expect { subject }.to raise_error(MyError)
end
expect { subject }.to raise_error(MyError) part is working as expected, but how should I assert the logging? With the example code above, RSpec will report the error on the raised error without asserting the logging.
Just put them both in the same it. Expect that it raises an error and logs it.
it "raises MyError and logs it" do
expect { subject }.to raise_error(MyError)
expect(Rails.logger).to have_received(:error).with(/some message with var1/)
end
Alternatively if you really want to check that it logs the error in a separate it you'll have to rescue the error. Otherwise your spec will fail (unhandled error)
it "logs the error" do
subject
rescue
ensure
expect(Rails.logger).to have_received(:error).with(/some message with var1/)
end

How do I test the rescue block of a method with rspec mocks 3.3

Help me make this test pass:
Here is an example of some rspec code,
class User
attr_accessor :count
def initialize
#count = 0
end
# sometimes raises
def danger
puts "IO can be dangerous..."
rescue IOError => e
#count += 1
end
#always raises
def danger!
raise IOError.new
rescue IOError => e
#count += 1
end
end
describe User do
describe "#danger!" do
it "its rescue block always increases the counter by one" do
allow(subject).to receive(:'danger!')
expect {
subject.danger!
}.to change(subject, :count).by(1)
end
end
describe "#danger" do
context "when it rescues an exception" do
it "should increase the counter" do
allow(subject).to receive(:danger).and_raise(IOError)
expect {
subject.danger
}.to change(subject, :count).by(1)
end
end
end
end
I've also created a fiddle with these tests in it, so you can just make them pass. Please help me test the rescue block of a method!
Background:
My original question went something like this:
I have a method, like the following:
def publish!(resource)
published_resource = resource.publish!(current_project)
resource.update(published: true)
if resource.has_comments?
content = render_to_string partial: "#{ resource.class.name.tableize }/comment", locals: { comment: resource.comment_content_attributes }
resource.publish_comments!(current_project, published_resource.id, content)
end
true
rescue Bcx::ResponseError => e
resource.errors.add(:base, e.errors)
raise e
end
And I want to test that resource.errors.add(:base, e.errors) is, in fact, adding an error to the resource. More generally, I want to test the rescue block in a method.
So I'd like to write code like,
it "collects errors" do
expect{
subject.publish!(training_event.basecamp_calendar_event)
}.to change(training_event.errors.messages, :count).by(1)
end
Of course, this raises an error because I am re-raising in the rescue block.
I've seen a few answers that use the old something.stub(:method_name).and_raise(SomeException), but rspec complains that this syntax is deprecated. I would like to use Rspec Mocks 3.3 and the allow syntax, but I'm having a hard time.
allow(something).to receive(:method_name).and_raise(SomeException)
would be the new allow syntax. Check out the docs for reference.
I was misunderstanding what the allow syntax is actually for. So to make my example specs pass, I needed to do this:
describe "#danger" do
context "when it rescues an exception" do
it "should increase the counter" do
allow($stdout).to receive(:puts).and_raise(IOError) # <----- here
expect {
subject.danger
}.to change(subject, :count).by(1)
end
end
end
This thing that I'm stubing is not the method, or the subject, but the object that might raise. In this case I stub $stdout so that puts will raise.
Here is another fiddle in which the specs are passing.

How to test exception raising in Rails/RSpec?

There is the following code:
def index
#car_types = car_brand.car_types
end
def car_brand
CarBrand.find(params[:car_brand_id])
rescue ActiveRecord::RecordNotFound
raise Errors::CarBrandNotFound.new
end
I want to test it through RSpec. My code is:
it 'raises CarBrandNotFound exception' do
get :index, car_brand_id: 0
expect(response).to raise_error(Errors::CarBrandNotFound)
end
CarBrand with id equaling 0 doesn't exist, therefore my controller code raises Errors::CarBrandNotFound, but my test code tells me that nothing was raised. How can I fix it? What do I wrong?
Use expect{} instead of expect().
Example:
it do
expect { response }.to raise_error(Errors::CarBrandNotFound)
end
In order to spec error handling, your expectations need to be set on a block; evaluating an object cannot raise an error.
So you want to do something like this:
expect {
get :index, car_brand_id: 0
}.to raise_error(Errors::CarBrandNotFound)
See Expect error for details.
I am a bit surprised that you don't get any exception bubbling up to your spec results, though.
get :index will never raise an exception - it will rather set response to be an 500 error some way as a real server would do.
Instead try:
it 'raises CarBrandNotFound exception' do
controller.params[:car_brand_id] = 0
expect{ controller.car_brand }.to raise_error(Errors::CarBrandNotFound)
end

rails rspec matcher for exception with a stub method

I am having trouble for the matcher to catch the exception. The controller method simply doing a REST call and get the fruit with the id and I want to test when REST give me an error respond which in rails is the JSON::ParserError. I want to test this case, so I stub out the REST call and raise the exception.
I know the fact that the stubbing work since I am getting that exact error. I believe that I just need a matcher to catch the error when calling the get
In Controller
def show
#fruit = FruitsService::Client.get_fruit(params[:id])
end
spec/controller/fruits_controller_spec.rb
describe '#show' do
before do
context 'when a wrong id is given' do
FruitsService::Client.any_instance
.stub(:get_fruit).with('wrong_id')
.and_raise(JSON::ParserError)
end
it 'receives 404 error code' do
get :show, {id: 'wrong_id'} <------ I think I might need a matcher for this ?
expect(FruitsService::Client.get_fruit('wrong_id')).to raise_error(JSON::ParserError)
end
end
This giving this
Failure/Error: get :show, {id: 'wrong_id'}
JSON::ParserError:
JSON::ParserError
When you want to test behavior, such as the raising of errors, you need to pass a block to expect instead of a parameter, as in:
it 'receives 404 error code' do
expect { get :show, {id: 'wrong_id'} }.to raise_error(JSON::ParserError)
end

Resources