配置Warden以用于RSpec控制器规范

我能够使用Devise的sign_in方法在我的控制器规范中登录用户。 但是现在我正在从我的应用程序中删除Devise,我不太确定如何使用Warden自己的类似function。

我应该如何设置spec/spec_helper.rb和相关的spec/support/*.rb spec/spec_helper.rb文件,以使Warden在控制器规格内运行得足够多?

我尝试使用以下内容在spec/support/warden.rb设置文件:

 RSpec.configure do |config| config.include Warden::Test::Helpers config.after do Warden.test_reset! end end 

然后我before调用类似于此beforevalidationuser工厂:

 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 

我很感激任何人都可以提供的任何指导。

您正在尝试做的事情存在一个基本问题。 Warden是一个Rack中间件,但是RSpec控制器规格甚至不包括Rack,因为这些类型的规范并不是为了运行你的完整应用程序堆栈,而只是你的控制器代码。 您可以使用单独的测试来测试您的中间件,但在这种情况下,我认为测试Warden本身是否有效是不合理的。

要测试您是否正确配置了Warden,您应该使用请求规范或集成规范(黄瓜,水豚或类似)。

虽然在技术上可以在控制器规范中模拟Warden,但我认为它并没有为您提供很多好处,同时显着增加了测试代码的复杂性。 请记住,Rack中间件旨在以透明的方式运行,以便您可以根据需要轻松地交换中间件。 实际上,你的控制器根本不应该直接依赖于Warden(实际上除了ApplicationController ),因此对你的控制器的Warden进行测试依赖是一个破坏封装的标志。

我最近遇到了同样的问题所以我希望这个评论有用。

我不认为这个问题适用于我的情况,但确实如此: 在控制器测试中对Warden进行调查

事实certificate,Warden没有被纳入RSpec控制器规范中,所以你需要做一些魔术才能完成它。

Kentaro Imai的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:RSpec.configure块中的spec/spec_helper.rb ,添加此行以包含新模块:

 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小费,我有一天(或在另一个生命中)喝啤酒!