Настройка 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
Буду признателен за любое руководство, которое может предоставить любой человек.
Ответы
Ответ 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
.
Кончик шляпы Кентаро Имаи, которого я должен пиву однажды (или в другой жизни)!