Подтвердить что ты не робот

Настройка Warden для использования в спецификациях контроллера RSpec

Я смог использовать метод 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 вызовы, подобные этому, для аутентификации a user factory:

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

Буду признателен за любое руководство, которое может предоставить любой человек.

4b9b3361

Ответ 1

Существует основная проблема с тем, что вы пытаетесь сделать. Warden - это промежуточное ПО Rack, но спецификации контроллера RSpec даже не включают Rack, поскольку эти типы спецификаций не предназначены для запуска полного стека приложений, а только для вашего кода контроллера. Вы можете протестировать ваше промежуточное ПО с отдельными тестами только для тех, но в этом случае я не думаю, что имеет смысл проверить, что сам Warden работает.

Чтобы проверить правильность настройки Warden, вы должны использовать спецификации запросов или спецификации интеграции (огурец, капибара или аналогичный).

Хотя технически возможно высмеять Warden в спецификациях контроллера, я думаю, что это не дает вам большой пользы, значительно увеличивая сложность вашего тестового кода. Имейте в виду, что промежуточное ПО Rack предназначено для прозрачной работы, так что легко и просто поменять промежуточное ПО. Фактически ваш контроллер не должен напрямую зависеть от Warden (за исключением, возможно, для ApplicationController), поэтому наличие тестовой зависимости от Warden для вашего контроллера является признаком сломанной инкапсуляции.

Недавно я столкнулся с этой проблемой, поэтому надеюсь, что этот комментарий будет полезен.

Ответ 2

Я не думал, что этот вопрос применим к моей ситуации, но он делает: Stubbing Warden на тестах контроллера

Как выясняется, 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 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? && [email protected](: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.

Кончик шляпы Кентаро Имаи, которого я должен пиву однажды (или в другой жизни)!