简体   繁体   English

测试使用CanCan和Devise与RSpec的视图

[英]Testing views that use CanCan and Devise with RSpec

I was trying to test a simple index view, which has following code inside: 我试图测试一个简单的索引视图,其中包含以下代码:

- if can? :destroy, MyModel
  %th Options

MyModelsController has following options (Inherited Resources + CanCan + Devise): MyModelsController有以下选项(Inherited Resources + CanCan + Devise):

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

When running specs, it crashes at the line - if can? :destroy, MyModel 在运行规格时,它会崩溃- if can? :destroy, MyModel - if can? :destroy, MyModel

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

There's no traceback, nothing to base on... 没有追溯,没有任何依据......

I thought that maybe I'm not authorized and signed when testing views, but Devise::TestHelpers should only be included in controller tests (and that's how I have it). 我想也许在测试视图时我没有被授权和签名,但是Devise::TestHelpers应该只包含在控制器测试中(这就是我的方法)。

I was trying to override method can? 我试图覆盖方法可以吗? in both Ability and the controller, but that gave no effect. Ability和控制器中,但没有产生任何影响。

This is described in the CanCan docs for controller testing , and can also be modified to apply to view specs. 这在控制器测试CanCan文档中有所描述,也可以修改为适用于查看规范。 Here's one way to do it: 这是一种方法:

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

In your spec_helper: 在您的spec_helper中:

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

In your view spec: 在您的视图规范中:

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

The 'before :each' code posted by zetetic doesn't work for me. zetetic发布的'before:each'代码对我不起作用。 My views bork on the 'can?' 我对'can?'的看法很开心 method because 'current_ability' in the view returns nil. 方法因为视图中的'current_ability'返回nil。 I fixed it by using this 'before :each' code instead: 我使用这个'before:each'代码修改了它:

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

The above code simulates a login. 上面的代码模拟了登录。

For new RSpec 3.0 syntax 对于新的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

The problem with the solution from the CanCan wiki is that it requires a @ability. can ... CanCan wiki解决方案的问题在于它需要@ability. can ... @ability. can ... in each example, which doesn't feel very DRY. @ability. can ...在每个例子中,感觉不是很干。

Moreover, it doesn't actually stub out the abilities themselves, but the method that returns the controller's ability. 而且,它实际上并没有隐藏能力本身,而是返回控制器能力的方法。 The ability is not a stub and consequently the abilities are checked. 能力不是存根,因此检查能力。

If you're using Rspec and want to test just the controller (and not it's abilities), here's how to stub it out: 如果您正在使用Rspec并且想要仅测试控制器(而不是它的能力),那么这里是如何将其存根:

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

This works because as_null_object returns truthy values for all methods, so the ability checking methods pass. 这是因为as_null_object为所有方法返回truthy值,因此能力检查方法通过。

Based on John Kloian's example I defined this useful helper: 根据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

My full spec/support/sign_in.rb looks like this: 我的完整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

I can now login a user the same way in feature, controller, and view specs: 我现在可以在功能,控制器和视图规范中以相同的方式登录用户:

user = create :user # Using FactoryBot
login_as user

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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