ruby-on-railsrspecseparation-of-concernsbefore-filter

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!


Solution

  • 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