sebastiangeiger
sebastiangeiger

Reputation: 3416

RSpec in Rails: How to skip a before_filter?

I am trying to test my controller and maintain separation of concerns.

The first concern is "Who is able to execute which action?"
I am using authlogic for authentication and be9's acl9 for authorization. But this should not matter, all my authorization concerns are handled in a before_filter. I am testing such a before_filter by something similar to this:

describe SomeModelsController, "GET to index (authorization)" do
  before(:each) do
    @siteadmin = mock_model(User)
    @siteadmin.stub!(:has_role?).with("siteadmin", nil).and_return(true)
  end

  it "should grant access to a siteadmin" do
    controller.should_receive(:current_user).at_least(:once).and_return(@siteadmin)
    get :index
    response.should be_success
  end
end

This spec is working just fine!

Now, the second concern is "Does the action do what it is supposed to do?"
This does not involve checking authorization. The best/cleanest solution would be skipping that before_filter all together and just do something like:

describe SomeModelsController, "GET to index (functional)" do
  it "should find all Models" do
    Model.should_receive(:find).with(:all)
  end
end

Without having to worry about which user with which role has to logged in first. Right now I solved it like that:

describe SomeModelsController, "GET to index (functional)" do
  before(:each) do
    @siteadmin = mock_model(User)
    @siteadmin.stub!(:has_role?).with("siteadmin", nil).and_return(true)
    controller.stub!(:current_user).and_return(@siteadmin)
   end
  
  it "should find all Models" do
    Model.should_receive(:find).with(:all)
  end
end

If I now decided that my siteadmin does not have the right to access the index action anymore, it would not only break one spec - namely the spec that HAS to break in such a case - but also the totally unrelated second spec.

I know this is basically a minor issue, but it would be nice if somebody could come up with an (elegant) solution!

Upvotes: 18

Views: 19021

Answers (3)

fx_
fx_

Reputation: 1932

Old question, but I had to solve this just recently. This will only work for Rails 3.1, for earlier versions you should replace _process_action_callbacks with filter_chain (untested)

You can simply delete a matching Proc from the filter chain like so (Test::Unit example):

class RandomControllerTest < ActionController::TestCase
  def setup
    @controller.class._process_action_callbacks.each do |f|
      @controller.class._process_action_callbacks.delete(f) if f.raw_filter.to_s.match(/**<match for your proc>**/)
    end
  end
end

Upvotes: 0

fernyb
fernyb

Reputation: 983

How about just not doing a GET request? Try calling the controller method by itself.

controller.index

Upvotes: -9

Baldu
Baldu

Reputation: 5774

To skip the before filter:

controller.class.skip_before_filter :name_of_method_used_as_before_filter

The one caveat (mentioned the docs) is that this will only work for method-reference filters, not procs.

Alternatively, you could stub current_user.has_role?

describe SomeModelsController, "GET to index (functional)" do
  before(:each) do
    controller.current_user.stub!(:has_role?).and_return(true)
  end

  it "should find all Models" do
    Model.should_receive(:find).with(:all)
  end
end

Upvotes: 36

Related Questions