【问题标题】:Testing views that use CanCan and Devise with RSpec测试使用 CanCan 和 Devise 与 RSpec 的视图
【发布时间】:2011-06-28 10:46:02
【问题描述】:

我正在尝试测试一个简单的索引视图,其中包含以下代码:

- if can? :destroy, MyModel
  %th Options

MyModelsController 有以下选项(继承资源 + CanCan + 设计):

class MyModelsController < ApplicationController
  inherit_resources
  nested_belongs_to :mymodel
  before_filter :authenticate_user!
  load_and_authorize_resource :project
  load_and_authorize_resource :mymodel, :through => :project

运行规范时,它在- if can? :destroy, MyModel 行崩溃

Failure/Error: render
   ActionView::Template::Error:
      undefined method `authenticate' for nil:NilClass

没有追溯,没有任何依据......

我想也许我在测试视图时没有被授权和签名,但Devise::TestHelpers 应该只包含在控制器测试中(这就是我的方式)。

我试图重写方法可以吗?在Ability 和控制器中,但这没有任何效果。

【问题讨论】:

    标签: ruby-on-rails ruby rspec devise cancan


    【解决方案1】:

    这在CanCan docs for controller testing 中有描述,也可以修改以应用于查看规范。这是一种方法:

    require 'spec_helper'
    
    describe "mymodel/index.html.erb" do
      before(:each) do
        assign(:my_model,mock_model(MyModel))
        @ability = Object.new
        @ability.extend(CanCan::Ability)
        controller.stub(:current_ability) { @ability }
      end
    
      context "authorized user" do
        it "can see the table header" do
          @ability.can :destroy, MyModel
          render
          rendered.should have_selector('th:contains("Options")')
        end
      end
    
      context "unauthorized user" do
        it "cannot see the table header" do
          render
          rendered.should_not have_selector('th:contains("Options")')
        end
      end
    end
    

    【讨论】:

    • 我不知道我是怎么错过的,但它工作得很好,谢谢!
    • 我还必须在 view.current_abilitycontroller 上存根,但这是完美的
    【解决方案2】:

    zetetic 发布的 'before :each' 代码对我不起作用。我对“罐头?”的看法不合时宜。方法,因为视图中的 'current_ability' 返回 nil。我改用这个“之前:每个”代码来修复它:

    @ability = Ability.new(user)
    assign(:current_ability, @ability)
    controller.stub(:current_user, user)
    view.stub(:current_user, user)
    

    以上代码模拟登录。

    【讨论】:

      【解决方案3】:

      在你的 spec_helper 中:

      config.include Devise::TestHelpers, :type => :view
      

      在您的视图规范中:

      controller.stub!(current_user: [some user])
      view.stub!(current_user: [some user])
      

      【讨论】:

      • 成功了!我曾尝试将 Devise::TestHelpers, :type => :controller 包含在内,但仍然有问题。包括帮助查看固定)
      • 效果很好。谢谢!
      【解决方案4】:

      对于新的 RSpec 3.0 语法

        before(:each) do
          assign(:my_model,mock_model(MyModel))
          @ability = Object.new.extend(CanCan::Ability)
          allow(controller).to receive(:current_ability).and_return(@ability)
        end
      

      【讨论】:

        【解决方案5】:

        CanCan wiki 中的解决方案的问题在于,它在每个示例中都需要 @ability. can ...,这感觉不是很干。

        此外,它实际上并没有将技能本身存根,而是返回控制器技能的方法。该能力不是存根,因此会检查这些能力。

        如果您正在使用 Rspec 并且只想测试控制器(而不是它的能力),以下是如何将其存根:

        before(:each) do
          ability = mock(:ability).as_null_object
          controller.stub(:current_ability).and_return(ability)
        end
        

        这是因为as_null_object为所有方法返回真值,所以能力检查方法通过了。

        【讨论】:

          【解决方案6】:

          基于 John Kloian 的示例,我定义了这个有用的助手:

          # spec/support/sign_in.rb
          module ViewSpecSignInHelper
            def login_as(user)
              allow(view).to       receive(:signed_in?).and_return   true
              allow(controller).to receive(:current_user).and_return user
            end
          end
          
          RSpec.configure do |config|
            config.include ViewSpecSignInHelper, type: :view
          end
          

          我完整的spec/support/sign_in.rb 看起来像这样:

          module ControllerSpecSignInHelper
            def login_as(user)
              sign_in(user)
            end
          end
          
          module FeatureSpecSignInHelper
            # See https://github.com/plataformatec/devise/wiki/How-To%3a-Test-with-Capybara
            include Warden::Test::Helpers
            Warden.test_mode!
          
            # A login_as(user) method is provided already!
          end
          
          module ViewSpecSignInHelper
            def login_as(user)
              allow(view).to       receive(:signed_in?).and_return   true
              allow(controller).to receive(:current_user).and_return user
            end
          end
          
          RSpec.configure do |config|
            config.include Devise::Test::ControllerHelpers, type: :controller
            config.include Devise::Test::ControllerHelpers, type: :view
          
            config.include ControllerSpecSignInHelper, type: :controller
            config.include FeatureSpecSignInHelper, type: :feature
            config.include ViewSpecSignInHelper, type: :view
          end
          

          我现在可以在功能、控制器和视图规范中以相同的方式登录用户:

          user = create :user # Using FactoryBot
          login_as user
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-06
            • 2013-05-30
            • 2011-10-07
            • 1970-01-01
            • 2015-02-20
            相关资源
            最近更新 更多