简体   繁体   中英

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?

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

ie 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM