我如何指定before_filters?

时间:2013-12-25 18:41:37

标签: ruby-on-rails ruby

我想用rspec指定我的控制器before_filter。 我想为它使用ActionController :: Testing :: ClassMethods#before_filters。

我在导轨c中得到了这些结果:

2.0.0p353 :006 > ActionController::Base.singleton_class.send :include, ActionController::Testing::ClassMethods
2.0.0p353 :003 > EquipmentController.before_filters

=> [:process_action,:process_action]

但实际上这是我行动的过滤器:

class EquipmentController < ApplicationController
  before_filter :authenticate_user!

有什么想法吗?

5 个答案:

答案 0 :(得分:7)

我找到了一种基于ActionController::Testing::ClassMethods#before_filter

源代码的方法

这将是我的规范:

describe EquipmentController do
  context 'authentication' do
    specify{ expect(EquipmentController).to filter(:before, with: :authenticate_user!, only: :index)}
  end
...

这是我在spec / support / matchers / filter.rb

中的匹配器
RSpec::Matchers.define :filter do |kind, filter|
  match do |controller|
    extra = -> (x) {true}
    if filter[:except].present?
      extra = -> (x) { x.options[:unless].include?( "action_name == '#{filter[:except]}'") }
    elsif filter[:only].present?
      extra = -> (x) { x.options[:if].include?( "action_name == '#{filter[:only]}'") }
    end
    controller._process_action_callbacks.find{|x|x.kind == kind && x.filter == filter[:with] && extra.call(x)}
  end
end

答案 1 :(得分:7)

这就是我在项目中所做的:

# spec/support/matchers/have_filters.rb
RSpec::Matchers.define :have_filters do |kind, *names|
  match do |controller|
    filters = controller._process_action_callbacks.select{ |f| f.kind == kind }.map(&:filter)
    names.all?{ |name| filters.include?(name) }
  end
end

# spec/support/controller_macros.rb
module ControllerMacros
  def has_before_filters *names
    expect(controller).to have_filters(:before, *names)
  end
end

# spec/spec_helper.rb
RSpec.configure do |config|
  config.include ControllerMacros, type: :controller
end

然后您可以在控制器规格中使用它,例如:

# spec/controllers/application_controller_spec.rb
require 'spec_helper'
describe ApplicationController do
  describe 'class' do
    it { has_before_filters(:authenticate_user) }
  end
end

答案 2 :(得分:1)

嗯,我不清楚你想测试什么,确切地说。听起来你正在测试

的存在
before_filter :authenticate_user!

但这只有在您知道:authenticate_user!按照您的意图行事时才有用。所以我建议进行双管齐下的测试攻击 -

  1. 测试#authenticate_user!根据您的规范行事
  2. 编写规范以确保在用户登录和注销时,使用before_filter保护的方法具有正确的行为。
  3. 惯用法,before_filter方法被声明为私有,因此您的规范可能如下所示:

    describe ApplicationController do
        describe 'GET /my_account' do # a method to which the before_filter applies
            subject { get :my_account }
    
            context 'with a logged-out session' do
                it 'redirects to the homepage' do
                    response.should redirect_to root_url
                end
            end
    
            context 'with a logged-in session' do
                # if you use FactoryGirl and have a spec helper method log_in() to set up the session
                before { log_in(FactoryGirl.create :user) }
                it { should render_template('my_account') } # etc...
            end
        end
    
        describe 'private #authenticate_user!' do
            subject { ApplicationController.send(:authenticate_user!) }
    
            it 'calls the authentication logic' do
                Authentication.expects(:attempt_login) # or whatever to verify the internals
                subject
            end
        end
    end
    

    通过单独测试,您可以验证是否保持了所需的行为,无论:authenticate_user!或其他登录内部的实现如何。并且,您可以在不过分依赖Rails内部的情况下实现这一目标!

    让我知道这是否有任何意义。我对StackOverflow比较陌生,所以很感激反馈!

答案 3 :(得分:0)

以下是您可以做的事情:

  1. authenticate_user!是您自己编写的方法,还是像Rails或Devise这样的宝石附带的方法?如果您没有编写该方法,则无需为其编写测试。只测试你自己编写的代码。宝石的作者已经为他们的代码编写了测试。

  2. 如果您想测试在控制器中调用方法authenticate_user!,您可以在规范中执行controller.should_receive(:authenticate_user!)以检查它是否正在被调用。

  3. 您还可以通过编写如下规范来检查EquipmentController是否调用before_filter:

  4. EquipmentController.should_receive(:before_filter).with(:authenticate_user!)

答案 4 :(得分:0)

shoulda-matchers gem为您提供use_after_actionuse_around_actionuse_before_action,这些都是简单的回调测试。无需编写自定义匹配器。