如何在控制器规范中禁用 before_action?

How to disable a before_action in a controller spec?

我在我的控制器规范中使用了它:

controller.class.skip_before_action

具体来说,在这种情况下:

controller.class.skip_before_action :require_authorisation_to_view_materials

材料控制器:

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

完整 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

你能看出这有什么问题吗?我其实不明白 controller.class.skip_before_action :require_authorisation_to_view_materials 有效,并且我在使用它之前发生了一些奇怪的事情(但我不确定这是否是罪魁祸首)。有人能解释一下这条线到底做了什么吗,如果我的

controller.class.before_action :require_authorisation_to_view_materials

真的有'switching'before_action'back on'在materials_controller的预期效果吗?我的规范代码看起来正常吗?

在进行控制器规范和伪造登录时,我喜欢使用期望来代替授权。

即你的情况:

  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

甚至更好

  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