How to Write Down the Rspec to Test Rescue Block

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

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.

Writing RSpec Test for Rescued Exception

Okay, so the answer here was extremely simple (thanks to @katafrakt for pointing it out). I'll put an answer here because I think it's interesting.

The problem is in my test:

it "indicates if a dependent gem is not found" do
expect {
Empiric.get_version("capybara")
}.to raise_error NoMethodError
end

Notice I'm calling get_version rather than gem_version. But ... then why didn't the test indicate this? At least by failing, which I would have expected.

The only way I found the issue was by adding in the message that the exception provides. To wit:

...
.to raise_error NoMethodError, "No gem loaded for capybara."

That execution then told me there was no get_version method defined. Specifically I got this:

expected NoMethodError with "No gem loaded for capybara",
got #<NoMethodError: undefined method `get_version' for Empiric:Module>

Obviously fixing the test to call the correct method did the trick. But what's less clear is why the test was originally indicating as passing when I was clearly calling an incorrect method. Not only an incorrect method, but a non-existent one. I have no get_version method in my code at all.

But notice I'm rescuing a "NoMethodError" in the first place. So maybe that's the case here? Maybe the fact of get_version itself being a NoMethodError was getting swallowed up by the actions of the test? It was only when I put in a specific message for my exception did that then force RSpec to realize that the actual NoMethodError -- with a different message -- was occurring.

I was tempted to delete the question since I clearly made a bone-headed mistake. But I feel the context of that mistake might be interesting enough for others looking at this. If moderators disagree and feel this should be deleted, no worries. Otherwise, my stupidity can stay enshrined for all to see.

How to test the rescue block in my model callback in RSpec?

no need to stub MyJob class

it "should use fallback to send e-mail after create when faktory is down" do
allow(MyJob).to receive(:perform_async).and_raise(Errno::ECONNREFUSED)
expect_any_instance_of(MyJob).to receive(:perform)
company_opt_out
end

but make sure your company_opt_out calls send_email method

Using RSpec how can I test the results of a rescue exception block

Use should_receive and should be_false:

context "an exception is thrown" do
before do
ExternalService.stub(:call) { raise Exception }
end

it "should log the exception and return false" do
c = Capturer.new
Logger.should_receive(:log_exception)
c.capture.should be_false
end
end

Also note that you should not be rescuing from Exception, but something more specific. Exception covers everything, which is almost definitely not what you want. At the most you should be rescuing from StandardError, which is the default.

How to test a simple rescue block in ruby

The code you've shown here is weird and I get the feeling we're missing context, but in general you can stub out a method to raise an error like so:

expect(output).to receive(:split).with("\n").and_raise(RuntimeError.new("some error"))

but this is sort of an ugly way to go about things. If the error is raised conditionally depending the type of output, then it's better to find a way to set that variable to an error-producing value. How to do that, I can't tell you without seeing the test of your code.

For example, say you wrapped all this code in a def add_fact(output) - then from your tests you could intentionally pass an error-causing value for the output, and you no longer need to stub split (which is a wierd thing to do). This pattern is known as "dependency injection".



Related Topics



Leave a reply



Submit