缩短版本:
使用omniauth gem for sinatra,我无法让rspec登录工作,并为后续请求保留会话。
根据http://benprew.posterous.com/testing-sessions-with-sinatra的建议,并关闭会话,我已将问题与此隔离开来:
app.send(:set, :sessions, false) # From http://benprew.posterous.com/testing-sessions-with-sinatra
get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
# last_request.session => {"uid"=>"222222222222222222222", :flash=>{:success=>"Welcome"}}
# last_response.body => ""
follow_redirect!
# last_request.session => {:flash=>{}}
# last_response.body => Html for the homepage, which is what I want
如何让rspec遵循重定向并保留会话变量?这在Sinatra有可能吗?
从http://benprew.posterous.com/testing-sessions-with-sinatra开始,似乎我必须在每次需要登录的get / post请求上发送会话变量,但这在重定向的情况下不起作用。
详细信息:
我正尝试使用以下设置在sinatra中使用omniauth gem:
spec_helper.rb
ENV['RACK_ENV'] = 'test'
# Include web.rb file
require_relative '../web'
# Include factories.rb file
require_relative '../test/factories.rb'
require 'rspec'
require 'rack/test'
require 'factory_girl'
require 'ruby-debug'
# Include Rack::Test in all rspec tests
RSpec.configure do |conf|
conf.include Rack::Test::Methods
conf.mock_with :rspec
end
web_spec.rb
describe "Authentication:" do
before do
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
:uid => '222222222222222222222',
:info => {
:email => "someone@example.com",
:name => 'Someone'
}
})
end
describe "Logging in as a new user" do
it "should work" do
get '/auth/google_oauth2/'
last_response.body.should include("Welcome")
end
end
end
尝试进行身份验证时,我收到<h1>Not Found</h1>
响应。我错过了什么?
在omniauth docs的Integration testing页面上,它提到添加两个环境变量:
before do
request.env["devise.mapping"] = Devise.mappings[:user]
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
但似乎仅适用于rails,因为我添加了
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
到我的规范中的before
块,我收到此错误:
Failure/Error: request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
ArgumentError:
wrong number of arguments (0 for 1)
编辑:
使用
调用get
get '/auth/google_oauth2/', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
似乎让我last_request.env["omniauth.auth"]
等于
{"provider"=>"google_oauth2", "uid"=>"222222222222222222222", "info"=>{"email"=>"someone@example.com", "name"=>"Someone"}}
看似正确,但last_response.body
仍然会返回
<h1>Not Found</h1>
答案 0 :(得分:3)
部分答案......
回调网址更好,添加了请求环境变量:
get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
follow_redirect!
last_response.body.should include("Welcome")
但是,这不适用于重定向后的会话,这是我的应用程序知道某人已登录所必需的。更新了问题以反映这一点。
答案 1 :(得分:0)
使用this gist(来自https://stackoverflow.com/a/3892401/111884)来存储会话数据,我得到了测试来存储会话,允许我将会话传递给进一步的请求。
可能有一种更简单的方法。
设置代码:
# Omniauth settings
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
:uid => '222222222222222222222',
:info => {
:email => "someone@example.com",
:name => 'Someone'
}
})
# Based on https://gist.github.com/375973 (from https://stackoverflow.com/a/3892401/111884)
class SessionData
def initialize(cookies)
@cookies = cookies
@data = cookies['rack.session']
if @data
@data = @data.unpack("m*").first
@data = Marshal.load(@data)
else
@data = {}
end
end
def [](key)
@data[key]
end
def []=(key, value)
@data[key] = value
session_data = Marshal.dump(@data)
session_data = [session_data].pack("m*")
@cookies.merge("rack.session=#{Rack::Utils.escape(session_data)}", URI.parse("//example.org//"))
raise "session variable not set" unless @cookies['rack.session'] == session_data
end
end
def login!(session)
get '/auth/google_oauth2/callback', nil, { "omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
session['uid'] = last_request.session['uid']
# Logged in user should have the same uid as login credentials
session['uid'].should == OmniAuth.config.mock_auth[:google_oauth2]['uid']
end
# Based on Rack::Test::Session::follow_redirect!
def follow_redirect_with_session_login!(session)
unless last_response.redirect?
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
end
get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url, "rack.session" => {"uid" => session['uid']} })
end
def get_with_session_login(path)
get path, nil, {"rack.session" => {"uid" => session['uid']}}
end
示例rspec代码:
describe "Authentication:" do
def session
SessionData.new(rack_test_session.instance_variable_get(:@rack_mock_session).cookie_jar)
end
describe "Logging in as a new user" do
it "should create a new account with the user's name" do
login!(session)
last_request.session[:flash][:success].should include("Welcome")
get_with_session_login "/"
follow_redirect_with_session_login!(session)
last_response.body.should include("Someone")
end
end
end