我怎么能指定before_filters?

我想用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]

但实际上这是我行动的filter:

 class EquipmentController < ApplicationController before_filter :authenticate_user! 

有任何想法吗?

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

 # 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 

我想出了一种基于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 

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

 before_filter :authenticate_user! 

但这只有在您知道:authenticate_user!时才有用:authenticate_user! 表现得像你想要的那样。 所以我建议双管齐下的测试攻击 –

  1. 测试#authenticate_user! 根据您的规格行事
  2. 编写规范以确保在用户登录和注销时使用before_filter保护的方法具有正确的行为。

习惯上, 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 

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

如果这有任何意义,请告诉我。 我对StackOverflow比较新,所以反馈非常感谢!

这是你可以做的:

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

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

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

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

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