Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RSpec: Which is the right place for testing authorization?

Where should I test authorization with RSpec?

When you create a Rails application with RSpec, there are three folders that seems to be adequate:

  • spec/routing
  • spec/requests
  • spec/controllers

In which one should I test if the user is logged in? Should I test in more than one spec type?

like image 588
fotanus Avatar asked Jun 19 '13 17:06

fotanus


1 Answers

There is a subtle distinction to your question. Authorization usually referrers to permissions that users have within the app. Authentication referrers to users signing up and logging in users.

As far as Authentication goes, I usually prefer to use integration/requests specs or acceptance/feature specs. Feature specs are preferred lately because the Capybara DSL (page and visit) are only available in feature specs. They used to be allowed in request specs until the 2.x upgrade.

I will test things like signing up, signing in and signing out. For example,

# signing_up_spec.rb

feature 'Signing up' do
  scenario 'Successful sign up' do
    visit '/'
    within 'nav' do
      click_link 'Sign up'
    end
    fill_in "Email", :with => "[email protected]"
    fill_in "Password", :with => "password"
    fill_in "Password confirmation", :with => "password"
    click_button "Sign up"
    page.should have_content("Please open the link to activate your account.")
  end
end

This allows you to test the higher level aspect and lets you see the different components (controllers, views, etc...) within your app working together. This is by definition an integration/acceptance test. I would do the same as above for signing_in_spec.rb and signing_out_spec.rb

Now for Authorization, I would choose to use controller specs. This allows you to test the individual actions a user has permission to access. These controller specs are more granular in nature and are by definition unit/function tests. For example, say you had a ticket resource and you want to test that only certain users can access some particular functionality

# tickets_controller_spec.rb

describe TicketsController do
  let(:user) { FactoryGirl.create(:confirmed_user) }
  let(:project) { FactoryGirl.create(:project) }
  let(:ticket) { FactoryGirl.create(:ticket, :project => project,
                                  :user => user) }

  context "standard users" do
    it "cannot access a ticket for a project" do
      sign_in(:user, user)
      get :show, :id => ticket.id, :project_id => project.id
      response.should redirect_to(root_path)
      flash[:alert].should eql("The project you were looking for could not be found.")
    end

    context "with permission to view the project" do
      before do
        sign_in(:user, user)
        define_permission!(user, "view", project)
      end

      def cannot_create_tickets!
        response.should redirect_to(project)
        flash[:alert].should eql("You cannot create tickets on this project.")
      end

      def cannot_update_tickets!
        response.should redirect_to(project)
        flash[:alert].should eql("You cannot edit tickets on this project.")
      end

      it "cannot begin to create a ticket" do
        get :new, :project_id => project.id
        cannot_create_tickets!
      end

      it "cannot create a ticket without permission" do
        post :create, :project_id => project.id
        cannot_create_tickets!
      end

      it "cannot edit a ticket without permission" do
        get :edit, { :project_id => project.id, :id => ticket.id }
        cannot_update_tickets!
      end

      it "cannot update a ticket without permission" do
        put :update, { :project_id => project.id,
                       :id => ticket.id,
                       :ticket => {}
                     }
        cannot_update_tickets!
      end

      it "cannot delete a ticket without permission" do
        delete :destroy, { :project_id => project.id, :id => ticket.id }
        response.should redirect_to(project)
        flash[:alert].should eql("You cannot delete tickets from this project.")
      end

      it "can create tickets, but not tag them" do
        Permission.create(:user => user, :thing => project, :action => "create tickets")
        post :create, :ticket => { :title => "New ticket!",
                                   :description => "Brand spankin' new",
                                   :tag_names => "these are tags"
                                 },
                      :project_id => project.id
        Ticket.last.tags.should be_empty
      end
    end
  end
end

I have found that the combination of rspec-rails, capybara and factory_girl_rails have served well in both types of testing within rails apps.

The examples above were taken from the Rails3Book repo on github. Take a look at the repo for more examples. It is a great way to see the possibilities you have when testing a rails app.

like image 177
fontno Avatar answered Sep 28 '22 08:09

fontno