Ruby on rails 会话变量与黄瓜故事
我正在为“注册”应用程序编写一些黄瓜故事,该应用程序有许多步骤 我宁愿像普通用户一样在控制器中完成每个动作,而不是编写一个Huuuge故事来同时涵盖所有步骤,这是不好的。我这里的问题是,我将在第一步中创建的帐户ID存储为会话变量,因此当访问步骤2、步骤3等时,将加载现有的注册数据 我知道能够在RSpec规范中访问Ruby on rails 会话变量与黄瓜故事,ruby-on-rails,testing,rspec,cucumber,webrat,Ruby On Rails,Testing,Rspec,Cucumber,Webrat,我正在为“注册”应用程序编写一些黄瓜故事,该应用程序有许多步骤 我宁愿像普通用户一样在控制器中完成每个动作,而不是编写一个Huuuge故事来同时涵盖所有步骤,这是不好的。我这里的问题是,我将在第一步中创建的帐户ID存储为会话变量,因此当访问步骤2、步骤3等时,将加载现有的注册数据 我知道能够在RSpec规范中访问控制器.session[…],但是当我在Cucumber stories中尝试这样做时,失败了,出现了以下错误(而且,我也在某个地方读到了这是一个反模式等): 使用controller.
控制器.session[…]
,但是当我在Cucumber stories中尝试这样做时,失败了,出现了以下错误(而且,我也在某个地方读到了这是一个反模式等):
使用controller.session[:whatever]或session[:whatever]
使用会话(:随便什么)
因此,似乎会话存储实际上是不可能的。我想知道的是,是否有可能(我想哪一个最好……):
get\u registration
)谢谢 模拟在黄瓜场景中是不好的——它们几乎是一种反模式 我的建议是编写一个实际让用户登录的步骤。我是这样做的
Given I am logged in as "auser@example.com"
Given /^I am logged in as "(.*)"$/ do |email|
@user = Factory(:user, :email => email)
@user.activate!
visit("/session/new")
fill_in("email", :with => @user.email)
fill_in("password", :with => @user.password)
click_button("Sign In")
end
我意识到实例变量@user
的形式有点糟糕,但我认为在登录/注销的情况下,拥有@user
肯定是有帮助的
有时我称之为
@current_user
我会重复丹皮克特的话,在Cucumber中,只要可能,就应该避免模仿。但是,如果您的应用程序没有登录页面,或者性能有问题,则可能需要直接模拟登录
这是一个丑陋的黑客,但它应该完成工作
Given /^I am logged in as "(.*)"$/ do |email|
@current_user = Factory(:user, :email => email)
cookies[:stub_user_id] = @current_user.id
end
# in application controller
class ApplicationController < ActionController::Base
if Rails.env.test?
prepend_before_filter :stub_current_user
def stub_current_user
session[:user_id] = cookies[:stub_user_id] if cookies[:stub_user_id]
end
end
end
Given/^我以“(.*”$/do |电子邮件的身份登录|
@当前用户=工厂(:用户,:电子邮件=>电子邮件)
cookies[:stub\u user\u id]=@current\u user.id
结束
#应用程序内控制器
类ApplicationController
我的理解是,您可以:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.session (NoMethodError)
在实例化请求之前访问会话[]时。在您的情况下,我可以想象,如果您在步骤防御中访问会话[]之前,将WebRat的访问一些现有路径
,错误就会消失
现在,不幸的是,会话似乎不能跨步骤持续(至少,我找不到方法),所以这一点信息无助于回答您的问题:)
因此,我想Ryan的
会话[:user\u id]=cookies[:stub\u user\u id]…
是一条出路。虽然,依我看,应用程序中与测试相关的代码听起来并不正确。我不知道这与原来的问题有多大关系,但我还是决定本着讨论的精神发布
我们有一个cucumber测试套件,运行时间超过10分钟,因此我们想进行一些优化。在我们的应用程序中,登录过程会触发许多与大多数场景无关的额外功能,因此我们希望通过直接设置会话用户id来跳过这些功能
上面Ryanb的方法运行良好,只是我们无法使用该方法注销。这使得我们的多用户故事失败了
我们最终创建了一个仅在测试环境中启用的“快速登录”路由:
# in routes.rb
map.connect '/quick_login/:login', :controller => 'logins', :action => 'quick_login'
以下是创建会话变量的相应操作:
# in logins_controller.rb
class LoginsController < ApplicationController
# This is a utility method for selenium/webrat tests to speed up & simplify the process of logging in.
# Please never make this method usable in production/staging environments.
def quick_login
raise "quick login only works in cucumber environment! it's meant for acceptance tests only" unless Rails.env.test?
u = User.find_by_login(params[:login])
if u
session[:user_id] = u.id
render :text => "assumed identity of #{u.login}"
else
raise "failed to assume identity"
end
end
end
#在logins_controller.rb中
类LoginsController“假定#{u.login}的身份”
其他的
引发“无法假定身份”
结束
结束
结束
对我们来说,这比使用cookies数组更简单。作为奖励,这种方法也适用于Selenium/Watir
缺点是我们在应用程序中包含了与测试相关的代码。就我个人而言,我不认为添加代码使应用程序更易于测试是一个巨大的罪恶,即使它确实增加了一点混乱。也许最大的问题是未来的测试作者需要弄清楚他们应该使用哪种类型的登录。有了无限的硬件性能,我们显然不会做任何这类事情。Re。Ryan的解决方案-您可以在env.rb文件中打开ActionController并将其放在那里,以避免放入生产代码库(感谢john@pivotal labs)
#在features/support/env.rb中
类ApplicationController
我使用了一个只测试的登录解决方案,如,但我在机架中完成所有操作,而不是创建一个新的控制器和路由<
# in routes.rb
map.connect '/quick_login/:login', :controller => 'logins', :action => 'quick_login'
# in logins_controller.rb
class LoginsController < ApplicationController
# This is a utility method for selenium/webrat tests to speed up & simplify the process of logging in.
# Please never make this method usable in production/staging environments.
def quick_login
raise "quick login only works in cucumber environment! it's meant for acceptance tests only" unless Rails.env.test?
u = User.find_by_login(params[:login])
if u
session[:user_id] = u.id
render :text => "assumed identity of #{u.login}"
else
raise "failed to assume identity"
end
end
end
# in features/support/env.rb
class ApplicationController < ActionController::Base
prepend_before_filter :stub_current_user
def stub_current_user
session[:user_id] = cookies[:stub_user_id] if cookies[:stub_user_id]
end
end
# in config/environments/cucumber.rb:
config.middleware.use (Class.new do
def initialize(app); @app = app; end
def call(env)
request = ::Rack::Request.new(env)
if request.params.has_key?('signed_in_user_id')
request.session[:current_user_id] = request.params['signed_in_user_id']
end
@app.call env
end
end)
# in features/step_definitions/authentication_steps.rb:
Given /^I am signed in as ([^\"]+)$/ do |name|
user = User.find_by_username(name) || Factory(:user, :username => name)
sign_in_as user
end
# in features/step_definitions/authentication_steps.rb:
Given /^I am not signed in$/ do
sign_in_as nil
end
module AuthenticationHelpers
def sign_in_as(user)
return if @current_user == user
@current_user = user
get '/', { 'signed_in_user_id' => (user ? user.to_param : '') }
end
end
World(AuthenticationHelpers)
rack_test_driver = Capybara.current_session.driver
cookie_jar = rack_test_driver.current_session.instance_variable_get(:@rack_mock_session).cookie_jar
@current_user = Factory(:user)
cookie_jar[:stub_user_id] = @current_user.id
# In features/step_definitions/authentication_steps.rb:
class SessionsController < ApplicationController
def create_with_security_bypass
if params.has_key? :user_id
session[:user_id] = params[:user_id]
redirect_to :root
else
create_without_security_bypass
end
end
alias_method_chain :create, :security_bypass
end
Given %r/^I am logged in as "([^"]*)"$/ do |username|
user = User.find_by_username(username) || Factory(:user, :username => username)
page.driver.post "/session?user_id=#{user.id}"
end
@user = Factory(:user) # FactoryGirl
sign_in @user # Devise
User.current.should == @user # SentientUser
rack_test_browser = Capybara.current_session.driver.browser
cookie_jar = rack_test_browser.current_session.instance_variable_get(:@rack_mock_session).cookie_jar
cookie_jar[:stub_user_id] = @current_user.id
authenticate_or_request_with_http_basic "My Authentication" do |user_name, password|
if Rails.env.test? && user_name == 'testuser'
test_authenticate(user_name, password)
else
normal_authentication
end
end