Back to Repositories

Testing InheritedResource Controller Loading and Authorization in CanCan

This test suite validates the functionality of CanCan’s InheritedResource module, focusing on resource loading and authorization in Rails controllers. The tests ensure proper handling of controller actions, parameter processing, and ability-based access control.

Test Coverage Overview

The test suite provides comprehensive coverage of InheritedResource functionality in CanCan, examining resource loading across different controller actions (show, new, index).

Key areas tested include:
  • Resource loading through controller methods
  • Parent resource handling
  • Association chain processing
  • Attribute initialization with ability conditions
  • Parameter override behavior

Implementation Analysis

The implementation uses RSpec’s behavior-driven development approach with extensive stubbing to isolate controller behavior. The tests leverage mock objects and stubs to control method responses and verify instance variable assignments.

Notable patterns include:
  • Controller class simulation
  • Parameter manipulation
  • Ability integration testing
  • Resource chain verification

Technical Details

Testing tools and configuration:
  • RSpec testing framework
  • Mock object stubbing
  • HashWithIndifferentAccess for parameter handling
  • Custom controller class implementation
  • CanCan ability integration
  • Instance variable verification

Best Practices Demonstrated

The test suite exemplifies several testing best practices in Ruby and RSpec.

Notable practices include:
  • Proper test isolation using before blocks
  • Consistent stubbing patterns
  • Clear test case organization
  • Focused test scenarios
  • Thorough edge case coverage
  • Explicit expectation setting

ryanb/cancan

spec/cancan/inherited_resource_spec.rb

            
require "spec_helper"

describe CanCan::InheritedResource do
  before(:each) do
    @params = HashWithIndifferentAccess.new(:controller => "projects")
    @controller_class = Class.new
    @controller = @controller_class.new
    @ability = Ability.new(nil)
    stub(@controller).params { @params }
    stub(@controller).current_ability { @ability }
    stub(@controller_class).cancan_skipper { {:authorize => {}, :load => {}} }
  end

  it "show should load resource through @controller.resource" do
    @params.merge!(:action => "show", :id => 123)
    stub(@controller).resource { :project_resource }
    CanCan::InheritedResource.new(@controller).load_resource
    @controller.instance_variable_get(:@project).should == :project_resource
  end

  it "new should load through @controller.build_resource" do
    @params[:action] = "new"
    stub(@controller).build_resource { :project_resource }
    CanCan::InheritedResource.new(@controller).load_resource
    @controller.instance_variable_get(:@project).should == :project_resource
  end

  it "index should load through @controller.association_chain when parent" do
    @params[:action] = "index"
    stub(@controller).association_chain { @controller.instance_variable_set(:@project, :project_resource) }
    CanCan::InheritedResource.new(@controller, :parent => true).load_resource
    @controller.instance_variable_get(:@project).should == :project_resource
  end

  it "index should load through @controller.end_of_association_chain" do
    @params[:action] = "index"
    stub(Project).accessible_by(@ability, :index) { :projects }
    stub(@controller).end_of_association_chain { Project }
    CanCan::InheritedResource.new(@controller).load_resource
    @controller.instance_variable_get(:@projects).should == :projects
  end

  it "should build a new resource with attributes from current ability" do
    @params[:action] = "new"
    @ability.can(:create, Project, :name => "from conditions")
    stub(@controller).build_resource { Struct.new(:name).new }
    resource = CanCan::InheritedResource.new(@controller)
    resource.load_resource
    @controller.instance_variable_get(:@project).name.should == "from conditions"
  end

  it "should override initial attributes with params" do
    @params.merge!(:action => "new", :project => {:name => "from params"})
    @ability.can(:create, Project, :name => "from conditions")
    stub(@controller).build_resource { Struct.new(:name).new }
    resource = CanCan::ControllerResource.new(@controller)
    resource.load_resource
    @controller.instance_variable_get(:@project).name.should == "from params"
  end
end