1

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.

Community
  • 1
  • 1
Ziggy
  • 21,845
  • 28
  • 75
  • 104

2 Answers2

4
allow(something).to receive(:method_name).and_raise(SomeException)

would be the new allow syntax. Check out the docs for reference.

p4sh4
  • 3,292
  • 1
  • 20
  • 33
  • This does not answer the question, which is "how do I test the rescue block of a method". Also, you linked to the 2.14 docs. Please see my question for a link to the relevant 3.3 docs. – Ziggy Aug 21 '15 at 15:13
1

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.

Ziggy
  • 21,845
  • 28
  • 75
  • 104
  • 1
    not to be pedantic but @p4sh4 answered your question about 13 hours ago. – engineerDave Aug 21 '15 at 15:59
  • 1
    I don't think he did! He provided a link to the docs, the same docs that I myself provided a link to. He included an example of the `allow` syntax, but not how to use it. After reading his answer, I didn't feel better equipped. It was only after constructing this minimal example that I was able to puzzle through this on my own. – Ziggy Aug 21 '15 at 16:23