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

Chr*_*ers 20 rspec ruby-on-rails warden ruby-on-rails-3

我能够使用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
Run Code Online (Sandbox Code Playgroud)

然后我有before类似的调用来验证user工厂:

before { login_as FactoryGirl.create(:user) }
Run Code Online (Sandbox Code Playgroud)

但这是我一直看到的错误:

NameError:
  undefined method `user' for nil:NilClass
Run Code Online (Sandbox Code Playgroud)

此错误追溯到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
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*ers 31

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

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

Kentaro ImaiWarden博客文章的测试助手特别有帮助.以下是我为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
Run Code Online (Sandbox Code Playgroud)

步骤2:spec/spec_helper.rb中,内RSpec.configure块,添加此行,以包括新的模块:

config.include Warden::Test::ControllerHelpers, type: :controller
Run Code Online (Sandbox Code Playgroud)

步骤3:要在before块中登录用户,请使用与此类似的语法:

before { warden.set_user FactoryGirl.create(:user) }
Run Code Online (Sandbox Code Playgroud)

第4步:确保您request.env['warden']在控制器中引用,而不是env['warden'].后者不适用于test环境中的控制器规范.

帽子给Kentaro Imai提示,我有一天(或在另一个生命中)喝啤酒!

  • @ X2theZ如果你使用`rails_warden` gem,你可以将`m.name =='Warden :: Manager'改为`m.name =='RailsWarden :: Manager'. (2认同)

bow*_*ior 15

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

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

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

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

  • 我很高兴这条评论很有帮助.一般来说,我建议你在测试变得困难时退后一步.这通常是一个好兆头,表明你想要完成的事情并不完全正确. (2认同)