我想用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!
有什么想法吗?
答案 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!
按照您的意图行事时才有用。所以我建议进行双管齐下的测试攻击 -
惯用法,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)
以下是您可以做的事情:
authenticate_user!
是您自己编写的方法,还是像Rails或Devise这样的宝石附带的方法?如果您没有编写该方法,则无需为其编写测试。只测试你自己编写的代码。宝石的作者已经为他们的代码编写了测试。
如果您想测试在控制器中调用方法authenticate_user!
,您可以在规范中执行controller.should_receive(:authenticate_user!)
以检查它是否正在被调用。
您还可以通过编写如下规范来检查EquipmentController是否调用before_filter:
EquipmentController.should_receive(:before_filter).with(:authenticate_user!)
答案 4 :(得分:0)
shoulda-matchers gem为您提供use_after_action
,use_around_action
和use_before_action
,这些都是简单的回调测试。无需编写自定义匹配器。