Meilan
Meilan

Reputation: 474

How to check if the inside method has been called using RSpec

I got a method to update the person by id:

def update_person(id)
  handle_exceptions do
     person = Person.find(id)
     #...other
  end
end

When this id doesn't exist, the handle_exception should be called. But how could I test it? The test I wrote is:

context 'not found the proposals' do
  subject {controller.send(:update_person, 3)}

  before do
    allow(Person).to receive(:find).and_raise(ActiveRecord::RecordNotFound)
    allow(subject).to receive(:handle_exceptions)
  end

  it 'calls handle_exceptions' do
    expect(subject).to have_received(:handle_exceptions)
  end
end

But it not works, I got a failure said: Failure/Error: expect(subject).to have_received(:handle_exceptions)

   ({:message=>"Not Found", :status=>:not_found}).handle_exceptions(*(any args))
       expected: 1 time with any arguments
       received: 0 times with any arguments

The handle_exceptions method is

def handle_exceptions
  yield
rescue ActiveRecord::RecordNotFound => e
  flash[:warning] = 'no record found'
  Rails.logger.error message: e.message, exception: e
  @error_data = { message: 'no record found', status: :not_found }
end

Upvotes: 0

Views: 1013

Answers (2)

Meilan
Meilan

Reputation: 474

After couple days working, I realized the reason I'm confused about it is I didn't figure out about 'who called this function', and I think it's the most important thing to know before test it. For the method like this:

class User::Controller
  def methodA
      methodB 
  end

  def methodB
     // ...
  end

The mistake that I made is I thought the methodB is called by methods, but it's not. It's called by the controller, and that's the reason that I can't make the test works. There's so many things need to learn, and I hope there's one day that I won't have a mistake like this and be able to help others.

Upvotes: 0

max
max

Reputation: 101811

The problem is that you are calling the method under test in the subject block.

subject {controller.send(:update_person, 3)}

This is actually called before the example runs and before the before block.

context 'not found the proposals' do
  before do
    allow(subject).to receive(:handle_exceptions)
  end

  it 'calls handle_exceptions' do
    controller.send(:update_person, "NOT A VALID ID")
    expect(subject).to have_received(:handle_exceptions)
  end
end

But as far as tests go this one is not good. You're testing the implementation of update_person and not the actual behavior. And you're calling the method with update_person.send(:update_person, 3) presumably to test a private method.

You should instead test that your controller returns a 404 response code when try to update with an invalid id. Also why you insist on stubbing Person.find is a mystery since you can trigger the exception by just passing an invalid id. Only stub when you actually have to.

Upvotes: 2

Related Questions