Configuring Warden for use in RSpec controller specs

前端 未结 2 1579
情深已故
情深已故 2020-12-01 12:37

I was able to use Devise\'s sign_in method to log in a user in my controller specs. But now that I\'m removing Devise from my application, I\'m not quite sure h

2条回答
  •  悲哀的现实
    2020-12-01 13:08

    I didn't think that this question applied to my situation, but it does: Stubbing Warden on Controller Tests

    As it turns out, Warden does not get included into RSpec controller specs, so you need to do some magic to finagle it in.

    Kentaro Imai's Controller test helpers for Warden blog post was particularly helpful. Here's how I got it working for RSpec.

    Step 1: Create spec/spec_helper/warden.rb and paste in these contents, which Kentaro derived from 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
    

    Step 2: In spec/spec_helper.rb, within the RSpec.configure block, add this line to include the new module:

    config.include Warden::Test::ControllerHelpers, type: :controller
    

    Step 3: To log in a user in a before block, use syntax similar to this:

    before { warden.set_user FactoryGirl.create(:user) }
    

    Step 4: Make sure that you reference request.env['warden'] in your controllers, not env['warden']. The latter will not work in controller specs in the test environment.

    Hat tip to Kentaro Imai, whom I owe a beer one day (or in another life)!

提交回复
热议问题