代码之家  ›  专栏  ›  技术社区  ›  Robbie

测试控制器时保持干燥,通过CanCan授权

  •  1
  • Robbie  · 技术社区  · 14 年前

    我正在使用RSpec为Rails项目追溯性地编写一些测试。

    我在用坎坎宝石提供授权。我决定写一个规范来测试 ability.rb 模型。 然后我继续测试我剩下的模型。

    我已经转到了控制器,我遇到了一个巨大的障碍:我正在重新测试我的能力!

    403 Forbidden .
    究其原因,是管制员基本上负责担心授权问题。

    知道 能力起作用,这就是 ability_spec.rb

    所以这个问题有两个方面:

    1. 我应该单独测试能力模型吗?
    2. 控制器测试是否应该考虑适当的权限?

    需要“特殊助手” 包括Devise::TestHelpers#以使您的规范可以访问helpers

    describe TokensController do
      before(:each) do
        @mock_user = User.new(:username => "bob", :email => "user@user.com", :password => "longpassword")
        @mock_user.role = "admin"
        sign_in @mock_user
        #Ability.stub!('can').and_return(true)
      end
      it "should let me see grids/:g_id/tokens index" do
        test_grid = mock_model(Grid)
        test_token = mock_model(Token)
        Grid.stub!(:find).and_return(test_grid)
        Token.stub!(:find).and_return(test_token)
        get 'index'
    
        a1 = Ability.new(@mock_user)
        a1.can?(:index, Token).should be_true # This line works fine; as it should
        puts response.status #This returns 403, which means CanCan::AccessDenied was raised
      end
    end
    

    谢谢,
    罗比

    5 回复  |  直到 14 年前
        1
  •  2
  •   Jesse Wolgamott    14 年前

    我确实单独测试了cancan模型,但是测试了在什么条件下它允许什么。

    我想如果你在做

    authorize! :take_over, @the_world
    

    你可以排除这个能力?类并让它响应true/false,测试控制器在无法继续时(更重要的是)如何处理。

        2
  •  4
  •   Sam Ritchie    14 年前

    不确定这是否对您来说太晚了,但是我遇到了同样的问题,并使用下面的代码示例解决了它--

    before do
      @user = Factory.create(:user)
        sign_in @user
    
        @abilities = Ability.new(@user)
        Ability.stub(:new).and_return(@abilities)
      end
    end
    

    @abilities.stub!(:can?).with(:destroy, regatta).and_return(true)

    或授予管理员权限:

    @abilities.stub!(:can?).and_return(false)

        3
  •  1
  •   graysonwright    11 年前

    类似于山姆的回答,但来自CanCan wiki的测试页面:

    控制器测试

    user = User.create!(:admin => true) # I recommend a factory for this
    # log in user however you like, alternatively stub `current_user` method
    session[:user_id] = user.id 
    get :index
    assert_template :index # render the template since he should have access
    

    或者,如果您想独立于Ability类中的内容来测试控制器行为,那么很容易用您想要的任何行为来忽略该能力。

    def setup
      @ability = Object.new
      @ability.extend(CanCan::Ability)
      @controller.stubs(:current_ability).returns(@ability)
    end
    
    test "render index if have read ability on project" do
      @ability.can :read, Project
      get :index
      assert_template :index
    end
    

        4
  •  1
  •   Moustafa Samir    8 年前

    我认为授权主要是为了 控制器 以确保您的授权在控制器上正常工作。所以要做到 干的 你可以实现你自己的 matcher 像这样使用

    let!(:user) {create :user}
    before { login_user_request user}
    
    it "grants admin access to show action" do
      expect{ get :show, {id: user.id} }.to be_authorized
    end
    it "denies user access to edit action" do
      expect{ get :edit, {id: user.id} }.to be_un_authorized
    end
    

    RSpec::Matchers.define :be_authorized do
      match do |block|
        block.call
        expect(response).to be_success
      end
    
      def supports_block_expectations?
        true
      end
    end
    
    RSpec::Matchers.define :be_un_authorized do
      match do |block|
        expect{
          block.call
        }.to raise_error(Pundit::NotAuthorizedError)
      end
    
      def supports_block_expectations?
        true
      end
    end
    
        5
  •  0
  •   Victor Pudeyev    13 年前

    为什么不包括

    can :manage, :all do
      user.is_ultrasuper == 1
    end
    

    在你的能力,然后有一个是超级参数在你的夹具用户之一:

    one: 
      id: 1 
      username: my_username 
      is_ultrasuper: 1
    

    然后在设置测试时登录此用户。在测试中,你应该可以做任何事情。