Eric M.
Eric M.

Reputation: 5539

RSpec: Testing rescue_from

How can I test rescue_from is RSpec? I'd like to make sure that if one of the exceptions is raised, that the controller correctly sets the flash and does the redirect. Is there a way to simulate the exception?

  rescue_from PageAccessDenied do
    flash[:alert] = "You do not have the necessary roles to access this page"
    redirect_to root_url
  end

  rescue_from CanCan::AccessDenied do |exception|
    flash[:alert] = exception.message
    redirect_to root_url
  end

Upvotes: 17

Views: 9819

Answers (2)

SMAG
SMAG

Reputation: 788

I went with a shared_example approach:

we want to avoid stubbing / mocking the initialize method and there are plenty of warning to remind you of that if you try. Instead we can leverage the new or exception methods, depending on how your error is being raised.

I have noticed that errors explicitly invoked by raise require the check to be on the exception method.

example code:

 def validate_params
   params.require(:param_key_1)
   params.require(:param_key_2)

   raise ExpectedError if something_wrong?
 end
shared_examples_for :no_errors_raised do
  it { expect { subject }.not_to raise_error }
end

shared_examples_for :rescued_from do |error_class, invocation_method: :new|
  before do
    # complains if the expected error is not invoked
    expect(error_class).to receive(invocation_method).and_call_original
  end

  # complains if the error is not rescued
  it_behaves_like :no_errors_raised
end

it_behaves_like :rescued_from, ActionController::ParameterMissing # => when param_key_1 or param_key_2 is missing
it_behaves_like :rescued_from, ExpectedError, method: :exception # => when something_wrong? == true

I have tested this locally and it seems to work fine when the rescue_from approach is used, therefore you would just call the same shared_example as follows:

context "PageAccessDenied" do
  # setup your test conditions
  # run the request

  it { expect(response).to redirect_to(root_url) }
  it { expect(flash[:alert]).to eq "You do not have the necessary roles to access this page" }
  it_behaves_like :rescued_from, PageAccessDenied
end

NOTE: don't forget to break up your expects into separate tests so that one failing ahead of the others doesn't hide additional test feedback / failures that can help with troubleshooting the failure.

Upvotes: 0

zetetic
zetetic

Reputation: 47548

Assuming that you have an authorize! method that raises the exception, you should be able to do something like this:

  describe "rescue_from exceptions" do
    it "rescues from PageAccessDenied" do
      controller.stub(:authorize!) { raise PageAccessDenied }
      get :index
      response.should redirect_to("/")
      flash[:alert].should == "You do not have the necessary roles to access this page"
    end
  end

Upvotes: 14

Related Questions