Аутентификация очереди в спецификации запроса
При написании спецификации запроса, как вы устанавливаете сеансы и/или методы контроллера-заглушки?
Я пытаюсь отключить проверку подлинности в своих тестах интеграции - rspec/requests
Здесь пример теста
require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/authentication_helpers'
describe "Messages" do
include AuthenticationHelpers
describe "GET admin/messages" do
before(:each) do
@current_user = Factory :super_admin
login(@current_user)
end
it "displays received messages" do
sender = Factory :jonas
direct_message = Message.new(:sender_id => sender.id, :subject => "Message system.", :content => "content", :receiver_ids => [@current_user.id])
direct_message.save
get admin_messages_path
response.body.should include(direct_message.subject)
end
end
end
Помощник:
module AuthenticationHelpers
def login(user)
session[:user_id] = user.id # session is nil
#controller.stub!(:current_user).and_return(user) # controller is nil
end
end
И ApplicationController, который обрабатывает аутентификацию:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
helper_method :logged_in?
protected
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!current_user.nil?
end
end
Почему невозможно получить доступ к этим ресурсам?
1) Messages GET admin/messages displays received messages
Failure/Error: login(@current_user)
NoMethodError:
undefined method `session' for nil:NilClass
# ./spec/requests/authentication_helpers.rb:3:in `login'
# ./spec/requests/message_spec.rb:15:in `block (3 levels) in <top (required)>'
Ответы
Ответ 1
Спецификация запроса представляет собой тонкую оболочку вокруг ActionDispatch::IntegrationTest
, которая не работает, как спецификации контроллера (которые завершают ActionController::TestCase
). Несмотря на то, что есть доступный метод сеанса, я не думаю, что он поддерживается (т.е. Вероятно, потому, что модуль, который входит в другие утилиты, также включает этот метод).
Я бы рекомендовал войти в систему, разместив все действия, которые вы используете для аутентификации пользователей. Если вы делаете пароль "password" (например) для всех фабрик Пользователя, вы можете сделать что-то вроде этого:
def login(user)
post login_path, :login => user.login, :password => 'password'
end
Ответ 2
Кстати, @David Chelimsky ответ может потребоваться небольшая настройка, если вы используете Devise. Что я делаю в своем тестировании интеграции/запросов (благодаря fooobar.com/questions/73380/...):
# file: spec/requests_helper.rb
def login(user)
post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end
Ответ 3
FWIW, портируя тесты Test:: Unit на RSpec, я хотел иметь возможность входить в систему с несколькими (разрабатывать) сеансами в моих спецификациях запросов. Потребовалось некоторое копание, но у меня это получилось. Использование Rails 3.2.13 и RSpec 2.13.0.
# file: spec/support/devise.rb
module RequestHelpers
def login(user)
ActionController::IntegrationTest.new(self).open_session do |sess|
u = users(user)
sess.post '/users/sign_in', {
user: {
email: u.email,
password: 'password'
}
}
sess.flash[:alert].should be_nil
sess.flash[:notice].should == 'Signed in successfully.'
sess.response.code.should == '302'
end
end
end
include RequestHelpers
А...
# spec/request/user_flows.rb
require 'spec_helper'
describe 'User flows' do
fixtures :users
it 'lets a user do stuff to another user' do
karl = login :karl
karl.get '/users'
karl.response.code.should eq '200'
karl.xhr :put, "/users/#{users(:bob).id}", id: users(:bob).id,
"#{users(:bob).id}-is-funny" => 'true'
karl.response.code.should eq '200'
User.find(users(:bob).id).should be_funny
bob = login :bob
expect { bob.get '/users' }.to_not raise_exception
bob.response.code.should eq '200'
end
end
Изменить: исправлена опечатка
Ответ 4
Вы также можете легко закрыть сессию.
controller.session.stub(:[]).with(:user_id).and_return(<whatever user ID>)
Все рубиновые специальные операторы действительно являются методами. Вызов 1+1
совпадает с 1.+(1)
, что означает, что +
- всего лишь метод. Аналогично, session[:user_id]
совпадает с вызывающим методом []
на session
, как session.[](:user_id)
Ответ 5
Я нашел это очень полезным для Devise: https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)