RSpec в Rails: как пропустить файл before_filter?

Я пытаюсь проверить свой контроллер и поддерживать разделение проблем.

Первой проблемой является "Кто может выполнить какое действие?"
 Я использую authlogic для аутентификации и be9 acl9 для авторизации. Но это не имеет значения, все мои вопросы авторизации обрабатываются в before_filter. Я тестирую такой before_filter чем-то похожим на это:

describe SomeModelsController, "GET to index (authorization)" do
  before(:each) do
    @siteadmin = mock_model(User)
    @siteadmin.stub!(:has_role?).with("siteadmin", nil).and_return(true)
  end

  it "should grant access to a siteadmin" do
    controller.should_receive(:current_user).at_least(:once).and_return(@siteadmin)
    get :index
    response.should be_success
  end
end

Эта спецификация работает отлично!

Теперь второе беспокойство: "Делает ли действие то, что он должен делать?"
Это не требует проверки авторизации. Лучшее/чистое решение будет пропускать, что before_filter все вместе и просто сделать что-то вроде:

describe SomeModelsController, "GET to index (functional)" do
  it "should find all Models" do
    Model.should_receive(:find).with(:all)
  end
end

Не нужно беспокоиться о том, какой пользователь должен выполнить первую роль. Прямо сейчас я решил это так:

describe SomeModelsController, "GET to index (functional)" do
  before(:each) do
    @siteadmin = mock_model(User)
    @siteadmin.stub!(:has_role?).with("siteadmin", nil).and_return(true)
    controller.stub!(:current_user).and_return(@siteadmin)
   end

  it "should find all Models" do
    Model.should_receive(:find).with(:all)
  end
end

Если я теперь решил, что мой сайт-админ не имеет права доступа к действию индекса больше, он не только сломает один spec, а именно спецификацию, которая должна сломаться в таком случае, - но и полностью несвязанная вторая спецификация.

Я знаю, что это в основном незначительная проблема, но было бы неплохо, если бы кто-нибудь мог придумать (элегантное) решение!

Ответы

Ответ 1

Чтобы пропустить фильтр до:

controller.class.skip_before_filter :name_of_method_used_as_before_filter

Единственное предостережение (упомянутое в документах) заключается в том, что это будет работать только для фильтров ссылок на методы, а не для procs.

В качестве альтернативы вы можете заглушить current_user.has_role?

describe SomeModelsController, "GET to index (functional)" do
  before(:each) do
    controller.current_user.stub!(:has_role?).and_return(true)
  end

  it "should find all Models" do
    Model.should_receive(:find).with(:all)
  end
end

Ответ 2

Старый вопрос, но я должен был решить это совсем недавно. Это будет работать только для Rails 3.1, для более ранних версий вы должны заменить _process_action_callbacks на filter_chain (untested)

Вы можете просто удалить подходящий Proc из цепочки фильтров (пример: Unit: Unit):

class RandomControllerTest < ActionController::TestCase
  def setup
    @controller.class._process_action_callbacks.each do |f|
      @controller.class._process_action_callbacks.delete(f) if f.raw_filter.to_s.match(/**<match for your proc>**/)
    end
  end
end

Ответ 3

Как просто не делать запрос GET? Попробуйте вызвать метод контроллера самостоятельно.

controller.index