我能够使用Devise的sign_in
方法在我的控制器规范中登录用户。但是现在我正在从我的应用程序中删除Devise,我不太确定如何使用Warden自己的类似功能。
我应该如何设置spec/spec_helper.rb
和相关的spec/support/*.rb
文件,以使Warden在控制器规格内充分运行?
我尝试使用以下内容在spec/support/warden.rb
设置文件:
RSpec.configure do |config|
config.include Warden::Test::Helpers
config.after do
Warden.test_reset!
end
end
然后,我有before
次类似的调用来验证user
工厂:
before { login_as FactoryGirl.create(:user) }
但这是我一直看到的错误:
NameError:
undefined method `user' for nil:NilClass
此错误追溯到控制器中的authenticate_user!
方法:
def authenticate_user!
redirect_to login_path, notice: "You need to sign in or sign up before continuing." if env['warden'].user.nil?
end
我很感激任何人都可以提供任何指导。
答案 0 :(得分:30)
我不认为这个问题适用于我的情况,但确实如此:Stubbing Warden on Controller Tests
事实证明,Warden没有被纳入RSpec控制器规范中,所以你需要做一些魔术才能完成它。
Kentaro Imai的Controller test helpers for Warden博客文章特别有帮助。以下是我为RSpec工作的方法。
第1步:创建spec/spec_helper/warden.rb
并粘贴这些内容,Kentaro派生自Devise:
module Warden
# Warden::Test::ControllerHelpers provides a facility to test controllers in isolation
# Most of the code was extracted from Devise's Devise::TestHelpers.
module Test
module ControllerHelpers
def self.included(base)
base.class_eval do
setup :setup_controller_for_warden, :warden if respond_to?(:setup)
end
end
# Override process to consider warden.
def process(*)
# Make sure we always return @response, a la ActionController::TestCase::Behavior#process, even if warden interrupts
_catch_warden {super} || @response
end
# We need to setup the environment variables and the response in the controller
def setup_controller_for_warden
@request.env['action_controller.instance'] = @controller
end
# Quick access to Warden::Proxy.
def warden
@warden ||= begin
manager = Warden::Manager.new(nil, &Rails.application.config.middleware.detect{|m| m.name == 'Warden::Manager'}.block)
@request.env['warden'] = Warden::Proxy.new(@request.env, manager)
end
end
protected
# Catch warden continuations and handle like the middleware would.
# Returns nil when interrupted, otherwise the normal result of the block.
def _catch_warden(&block)
result = catch(:warden, &block)
if result.is_a?(Hash) && !warden.custom_failure? && !@controller.send(:performed?)
result[:action] ||= :unauthenticated
env = @controller.request.env
env['PATH_INFO'] = "/#{result[:action]}"
env['warden.options'] = result
Warden::Manager._run_callbacks(:before_failure, env, result)
status, headers, body = warden.config[:failure_app].call(env).to_a
@controller.send :render, :status => status, :text => body,
:content_type => headers['Content-Type'], :location => headers['Location']
nil
else
result
end
end
end
end
end
第2步:在spec/spec_helper.rb
RSpec.configure
块中,添加此行以包含新模块:
config.include Warden::Test::ControllerHelpers, type: :controller
第3步:要在before
块中登录用户,请使用与此类似的语法:
before { warden.set_user FactoryGirl.create(:user) }
第4步:确保您在控制器中引用request.env['warden']
,而不是env['warden']
。后者不适用于test
环境中的控制器规范。
向Kentaro Imai提示,我有一天(或在另一个生命中)喝啤酒!
答案 1 :(得分:15)
您尝试做的事情存在一个基本问题。 Warden是一个Rack中间件,但是RSpec控制器规格甚至不包括Rack,因为这些类型的规范并不是为了运行你的完整应用程序堆栈,而只是你的控制器代码。您可以使用单独的测试来测试您的中间件,但在这种情况下,我认为测试Warden本身是否有效是不合理的。
要测试您是否正确配置了Warden,您应该使用请求规格或集成规格(黄瓜,水豚或类似物)。
尽管在技术上可以在控制器规范中模拟Warden,但我认为它并没有为您提供很多好处,同时显着增加了测试代码的复杂性。请记住,Rack中间件旨在以透明的方式运行,以便您可以根据需要轻松地交换中间件。实际上,您的控制器根本不应该直接依赖于Warden(实际上除了ApplicationController
之外),因此对控制器的Warden具有测试依赖性是封装破坏的标志。
我最近遇到了同样的问题,所以我希望这个评论有用。