在编写请求规范时,如何设置会话和/或存根控制器方法?我正在尝试在我的集成测试中排除身份验证 - 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)>'
请求规范是对 ActionDispatch::IntegrationTest
的薄包装,它不像控制器规范(包装 ActionController::TestCase
)那样工作。尽管有可用的会话方法,但我认为它不受支持(即它可能存在,因为其他实用程序包含的模块也包含该方法)。
我建议通过发布您用于验证用户身份的任何操作来登录。如果您为所有用户工厂设置密码“密码”(例如),那么您可以执行以下操作:
def login(user) post login_path, :login => user.login, :password => 'password' end
设计用户注意...
顺便说一句,如果您使用 Devise,@David Chelimsky 的答案可能需要稍作调整。我在集成/请求测试中所做的事情(感谢 this StackOverflow post):
# file: spec/requests_helper.rb
# Rails 6
def login(user)
post user_session_path, params: {
user: {
email: user.email, password: user.password
}
}
follow_redirect!
end
# Rails 5 or older
def login(user)
post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end
config/routes.rb
文件中有 devise_for :users
。如果您指定了不同的内容,则必须相应地调整您的代码。
'user[email]' => user.email
更改为 'user[username]' => user.username
,因为我的应用程序使用用户名而不是电子邮件作为登录名。
sign_in
测试助手。您可能会发现传入用户以控制控制器中的 current_user
响应很有用。使用工厂和已确认特征的示例:let(:user) { create(:user, :confirmed) } 在 { sign_in(user) } 之前。不要忘记 include Devise::Test::IntegrationHelpers
;)
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
编辑:修正错字
你也可以很容易地存根会话。
controller.session.stub(:[]).with(:user_id).and_return(<whatever user ID>)
所有 ruby 特殊运算符确实是方法。调用 1+1
与 1.+(1)
相同,这意味着 +
只是一个方法。同样,session[:user_id]
与在 session
上调用方法 []
相同,如 session.[](:user_id)
不定期副业成功案例分享
ActionDispatch::IntegrationTest
旨在模拟一个或多个用户通过浏览器进行交互,而无需使用真实的浏览器。在单个示例中可能有多个用户(即会话)和多个控制器,并且会话/控制器对象是最后一个请求中使用的对象。在请求之前您无权访问它们。page.driver.post
与 Capybara 一起使用page.driver.post
可能是一种反模式,根据 Jonas Nicklas 在 Capybara and testing APIs 中的说法)