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 Hoffman wrote:
Hey guys. My ApplicationController rescues
Mongoid::Errors::DocumentNotFound errors like this:

class ApplicationController<  ActionController::Base
   rescue_from Mongoid::Errors::DocumentNotFound,
     :with =>  :resource_not_found

   protected

   def resource_not_found(error)
     flash[:alert] = t('errors.resource_not_found')
     redirect_to root_url
   end
end

Obviously, I need specs for this. However, I can't figure out how this
should be specced.

Should I create a shared example group that expects the flash-alert
and redirect, and use it in every controller action that could
potentially raise this error? This seems right, but will be verbose.


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

_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to