I had a similar problem in the past (with RSpec 1.x). I tend to create
what I call a BaseController in each namespace specifically for shared
behavior. The best example of this is
class Admin::BaseController < ApplicationController
before_filter :require_logged_in
before_filter :require_admin
protected
def require_admin
redirect_to error_path(403) unless
current_user.is_administrator
end
end
[:require_logged_in is defined in ApplicationController because it is
also used by User::BaseController.]
More comments inline.
On 2010-11-20 10:12 PM, Nick H. wrote:
flash[:alert] = t('errors.resource_not_found')
I started to go this route as it seemed most appropriate to spec a
controller’s behavior in that controller, but since the behavior was
going to be the same in all such controllers, a shared example made
perfect sense. I was happily specing against the index action of such
controllers, until I came to one that didn’t have an index action.
Instead of putting in one just to be able to test, I tried figuring out
how to use an arbitrary action in the shared example. But that became
too complicated very quickly. There very well might be a way to do this,
but I was unable to get to it.
Or, in each controller that could raise this error, should I create
one example group that raises this error and expects the flash-alert
and redirect? This seems right because the rescuing behaviour exists
within the controller rather than each action. However, it ignores the
possibility of an action rescuing the error.
Could you not also spec it in actions that you see as possible sources
of the exception? This approach would certainly be most verbose, but
maybe you should start here and see what pattern develops that could
lead you down the proper path of refactoring.
Should I create a dummy controller with an action that raises this
error, and spec that? This would be akin to speccing
ApplicationController, though indirectly. This method was my first
inclination, but fails to provide specs for the other controllers that
inherit the rescuing behaviour.
Ultimately, I went this direction. I more or less applied the logic that
I would with a plugin: if the plugin is well tested and I trust it, do I
really need to test it wherever it is used? Here is what I ended up
with:
require ‘controller_helper’
describe Admin::BaseController do
should_descend_from ApplicationController
should_have_before_filter :require_logged_in
should_have_before_filter :require_admin
end
class Admin::BogusController < Admin::BaseController
def index
render :nothing => true
end
end
ActionController::Routing::Routes.draw do |map|
map.namespace :admin do |admin|
admin.resources :bogus
end
end
describe Admin::BogusController do
context ‘when the user is not logged in’ do
before(:each) { get :index }
it 'should redirect to login path' do
response.should redirect_to(login_path)
end
end
context 'when accessed by administrative users' do
before :each do
setup_admin
get :index
end
it 'should not redirect to error path 403' do
response.should_not redirect_to(error_path(403))
end
should_succeed
end
context 'when accessed by non-administrative users' do
before :each do
setup_user
get :index
end
it 'should redirect to error path 403' do
response.should redirect_to(error_path(403))
end
end
end
I’m not sure if this is what I’d do today, but this did solve my problem
before. My preference would have been the shared examples, but I don’t
want my tests to be too complicated.
Peace,
Phillip