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"
)
Related
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
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.
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
I have method like this
def className
def method_name
some code
rescue
some code and error message
end
end
So, How to write down the rspec to test rescue block..?
If you want to rescue, it means you expect some code to raise some kind of exception.
You can use RSpec stubs to fake the implementation and force an error. Assuming the execution block contains a method that may raise
def method_name
other_method_that_may_raise
rescue => e
"ERROR: #{e.message}"
end
hook the stub to that method in your specs
it " ... " do
subject.stub(:other_method_that_may_raise) { raise "boom" }
expect { subject.method_name }.to_not raise_error
end
You can also check the rescue handler by testing the result
it " ... " do
subject.stub(:other_method_that_may_raise) { raise "boom" }
expect(subject.method_name).to eq("ERROR: boom")
end
Needless to say, you should raise an error that it's likely to be raised by the real implementation instead of a generic error
{ raise FooError, "boom" }
and rescue only that Error, assuming this is relevant.
As a side note, in Ruby you define a class with:
class ClassName
not
def className
as in your example.
you can stub with return error
for example you have class with method like this :
class Email
def self.send_email
# send email
rescue
'Error sent email'
end
end
so rspec for raising error is
context 'when error occures' do
it 'should return error message' do
allow(Email).to receive(:send_email) { err }
expect(Email.send_email).to eq 'Error sent email brand'
end
end
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