21

Devise のsign_inメソッドを使用して、コントローラーの仕様でユーザーをログインさせることができました。しかし、Devise をアプリケーションから削除したので、Warden だけで同様の機能を動作させる方法がよくわかりません。

コントローラーの仕様内で Warden を十分に実行するには、どのようにセットアップspec/spec_helper.rbおよび関連ファイルを設定すればよいですか?spec/support/*.rb

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

誰でも提供できるガイダンスをいただければ幸いです。

4

2 に答える 2

31

この質問が私の状況に当てはまるとは思いませんでしたが、当てはまります: Controller Tests の Stubbing Warden

結局のところ、Warden は RSpec コントローラーの仕様に含まれていないため、それを仕上げるためにいくつかの魔法を行う必要があります。

Kentaro ImaiController 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.rbRSpec.configureブロック内に次の行を追加して、新しいモジュールを含めます。

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

ステップ 3:ブロック内のユーザーをログインするには、次のbeforeような構文を使用します。

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

ステップ 4:request.env['warden']ではなく、コントローラーで参照していることを確認してくださいenv['warden']。後者は、test環境内のコントローラー仕様では機能しません。

ある日(または別の人生で)ビールを借りている今井健太郎に敬意を表します!

于 2012-11-16T19:09:50.810 に答える