Тестирование представлений, использующих CanCan и Devise с RSpec - PullRequest
20 голосов
/ 16 февраля 2011

Я пытался протестировать простое представление индекса, в котором есть следующий код:

- if can? :destroy, MyModel
  %th Options

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

При запуске спецификаций происходит сбой на линии - if can? :destroy, MyModel

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

Нет трассировки, нет оснований для ...

Я подумал, что, возможно, я не авторизован иподписывается при тестировании представлений, но Devise::TestHelpers следует включать только в тесты контроллера (и вот как у меня это получается).

Я пытался переопределить метод, может?как в Ability, так и в контроллере, но это не дало эффекта.

Ответы [ 5 ]

29 голосов
/ 17 февраля 2011

Это описано в документации CanCan для тестирования контроллера , и также может быть изменено для применения для просмотра спецификаций.Вот один из способов сделать это:

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
5 голосов
/ 14 января 2012

Код «до: каждый», опубликованный Zetetic, не работает для меня.Мои взгляды борка на "может?"метод, потому что 'current_ability' в представлении возвращает ноль.Я исправил это, используя вместо этого код «before: each»:

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

Приведенный выше код имитирует логин.

4 голосов
/ 03 мая 2013

В вашем spec_helper:

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

На ваш взгляд спец:

controller.stub!(current_user: [some user])
view.stub!(current_user: [some user])
2 голосов
/ 04 января 2014

Для нового синтаксиса 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
1 голос
/ 09 июня 2013

Проблема с решением из вики CanCan заключается в том, что для каждого примера требуется @ability. can ..., что не очень уж СУХО.

Более того, он на самом деле не заглушает сами способности, а метод, который возвращает способность контроллера. Способность не является заглушкой, и, следовательно, способности проверяются.

Если вы используете Rspec и хотите протестировать только контроллер (а не его возможности), вот как его заглушить:

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

Это работает, потому что as_null_object возвращает истинные значения для всех методов, поэтому методы проверки способности проходят.

...