Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to disable a before_action in a controller spec?

I've employed the use of this in my controller spec:

controller.class.skip_before_action

Specifically, in this case:

controller.class.skip_before_action :require_authorisation_to_view_materials

MaterialsController:

class MaterialsController < ApplicationController
  before_action :set_material, only: [:show, :edit, :update, :destroy]
  before_action :require_authorisation_to_view_materials, only: [:index, :show]


  def require_authorisation_to_view_materials # For Materials Page
    unless user_signed_in? && current_user.can_view_materials?
      redirect_to root_path, alert: "You are not authorised to view the Materials page."
    end
  end

  # GET /materials
  # GET /materials.json
  def index
    @materials = Material.all
  end

  # GET /materials/1
  # GET /materials/1.json
  def show
  end

  # GET /materials/new
  def new
    @material = Material.new
  end

  # GET /materials/1/edit
  def edit
  end

  # POST /materials
  # POST /materials.json
  def create
    @material = Material.new(material_params)

    respond_to do |format|
      if @material.save
        format.html { redirect_to materials_path, notice: 'Material was successfully created.' }
        format.json { render :show, status: :created, location: @material }
      else
        format.html { render :new }
        format.json { render json: @material.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /materials/1
  # PATCH/PUT /materials/1.json
  def update
    respond_to do |format|
      if @material.update(material_params)
        format.html { redirect_to materials_path, notice: 'Material was successfully updated.' }
        format.json { render :show, status: :ok, location: @material }
      else
        format.html { render :edit }
        format.json { render json: @material.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /materials/1
  # DELETE /materials/1.json
  def destroy
    @material.destroy
    respond_to do |format|
      format.html { redirect_to materials_url, notice: 'Material was successfully deleted.' }
      format.json { head :no_content }
    end
  end


  private
    # Use callbacks to share common setup or constraints between actions.
    def set_material
      @material = Material.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def material_params
      params.require(:material).permit(:title, :level, :description, :link)
    end

end

And the full materials_controller_spec:

require "rails_helper.rb"

describe MaterialsController do
    before :each do 
        controller.class.skip_before_action :require_authorisation_to_view_materials
    end 

    after :each do
        controller.class.before_action :require_authorisation_to_view_materials
    end

    describe "GET #index" do 
        it "populates an array of materials (@materials)" do 
            material1, material2 = (FactoryGirl.create :material), (FactoryGirl.create :material)
            get :index
            expect(assigns(:materials)).to eq([material1, material2])
        end

        it "renders the index view" do 
            get :index
            expect(response).to render_template :index
        end 
    end

    describe "GET #show" do 
        it "assigns the requested material to @material" do 
            material = FactoryGirl.create :material
            get :show, id: material
            expect(assigns(:material)).to eq(material)
        end

        it "renders the #show view" do
            get :show, id: FactoryGirl.create(:material)
            expect(response).to render_template :show
        end
    end

    describe "POST #create" do 
        context "with VALID attributes" do
            it "creates new material" do 
                expect {
                    post :create, material: FactoryGirl.attributes_for(:material)
                }.to change(Material, :count).by(1)
            end

            it "redirects to the materials page" do
                post :create, material: FactoryGirl.attributes_for(:material)
                expect(response).to redirect_to :materials
            end
        end

        context "with INvalid attributes" do 
            it "does not create new material" do 
                expect {
                    post :create, material: FactoryGirl.attributes_for(:invalid_material)
                }.to_not change(Material, :count)
            end

            it "re-renders the #new method" do 
                post :create, material: FactoryGirl.attributes_for(:invalid_material)
                expect(response).to render_template :new
            end
        end
    end

    describe "PUT #update" do 
        before :each do 
            @material = FactoryGirl.create :material, title: "Title", level: "B2", description: "blah blah", link: "Dropbox Link"
        end

        context "valid attributes" do 
            it "locates the requested @material" do 
                put :update, id: @material, material: FactoryGirl.attributes_for(:material)
                expect(assigns :material).to eq @material
            end

            it "changes @material's attributes" do 
                put :update, id: @material,
                    material: FactoryGirl.attributes_for(:material, title: "Title", level: "A1", description: "blah blah", link: "Dropbox Link")
                @material.reload
                expect(@material.title).to              eq("Title")
                expect(@material.level).to              eq("A1")
                expect(@material.description).to    eq("blah blah")
            end

            it "redirects to the materials page" do 
                put :update, id: @material, material: FactoryGirl.attributes_for(:material)
                expect(response).to redirect_to :materials
            end
        end

        context "invalid attributes" do 
            it "locates the requested @material" do 
                put :update, id: @material, material: FactoryGirl.attributes_for(:invalid_material)
                expect(assigns :material).to eq @material
            end

            it "does not change @material's attributes" do 
                put :update, id: @material,
                    material: FactoryGirl.attributes_for(:material, title: nil, level: "B1", description: "description", link: "Dropbox Link")
                @material.reload
                expect(@material.title).to              eq("Title")
                expect(@material.level).to_not      eq("B1")
                expect(@material.description).to    eq("blah blah")
            end

            it "re-renders the edit method" do 
                put :update, id: @material, material: FactoryGirl.attributes_for(:invalid_material)
                expect(response).to render_template :edit
            end
        end
    end

    describe "DELETE destroy" do 
        before :each do 
            @material = FactoryGirl.create :material
        end

        it "deletes the material" do 
            expect{
                delete :destroy, id: @material
            }.to change(Material, :count).by(-1)
        end

        it "redirects to materials#index" do 
            delete :destroy, id: @material
            expect(response).to redirect_to :materials
        end
    end
end

Can you see anything wrong with this? I don't actually understand how the controller.class.skip_before_action :require_authorisation_to_view_materials works, and I've had some weird things happen before when using this (but I'm not sure if this was to blame). Could someone explain what this line does exactly, and if my

controller.class.before_action :require_authorisation_to_view_materials

does actually have the intended effect of 'switching' the before_action 'back on' in the materials_controller? Does my spec code look ok?

like image 231
Yorkshireman Avatar asked May 19 '15 13:05

Yorkshireman


People also ask

How do I remove or disable the action’s controller?

After you determine the required Action’s Controller, follow the steps below to remove or disable the Action: Override the Controller ‘s OnActivated method. Use the GetController method or the Frame. Controllers ( Window.Controllers) property to access the Controller.

What is an action in a controller?

An action (or action method) is a method on a controller which handles requests. Controllers logically group similar actions together. This aggregation of actions allows common sets of rules, such as routing, caching, and authorization, to be applied collectively. Requests are mapped to actions through routing. By convention, controller classes:

How do I get the action of a controller in Java?

Use the GetController method or the Frame. Controllers ( Window.Controllers) property to access the Controller. Use the Actions property of the Controller class ( YourController.Actions ["YourActionId"]) or built-in Controller properties (for example, NewObjectViewController.NewObjectAction) to access an Action.

How do I enable/disable actions based on a conditional appearance?

Use the Conditional Appearance module. You can enable and disable Actions based on the specified rules (for example, disable an Action based on business object properties). Refer to the following topic for more information: Declare Conditional Appearance Rules in Code.


1 Answers

When doing controller specs and faking login I like to use an expectation to stub out authorisation instead.

i.e. in your situation:

  require "rails_helper.rb"
  describe MaterialsController do

    before :each do 
      allow(controller).to receive(:require_authorisation_to_view_materials).and_return(true)
    end 

    #..snip
  end

Or even better

  require "rails_helper.rb"
  describe MaterialsController do

    before :each do 
      allow(controller).to receive(:current_user).and_return(FactoryGirl.create(:admin_user)
    end 

    #..snip
  end
like image 112
Chris O'Sullivan Avatar answered Sep 26 '22 01:09

Chris O'Sullivan