Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 会话变量与黄瓜故事_Ruby On Rails_Testing_Rspec_Cucumber_Webrat - Fatal编程技术网

Ruby on rails 会话变量与黄瓜故事

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.

我正在为“注册”应用程序编写一些黄瓜故事,该应用程序有许多步骤

我宁愿像普通用户一样在控制器中完成每个动作,而不是编写一个Huuuge故事来同时涵盖所有步骤,这是不好的。我这里的问题是,我将在第一步中创建的帐户ID存储为会话变量,因此当访问步骤2、步骤3等时,将加载现有的注册数据

我知道能够在RSpec规范中访问
控制器.session[…]
,但是当我在Cucumber stories中尝试这样做时,失败了,出现了以下错误(而且,我也在某个地方读到了这是一个反模式等):

使用controller.session[:whatever]或session[:whatever]

使用会话(:随便什么)

因此,似乎会话存储实际上是不可能的。我想知道的是,是否有可能(我想哪一个最好……):

  • 模拟会话存储等
  • 在控制器中有一个方法并将其删除(例如分配实例变量的
    get\u registration
  • 我已经浏览了RSpec的书(嗯,略读过)和WebRat等,但我还没有找到我问题的答案

    更清楚一点,注册过程更像是一个状态机——例如,用户在注册完成之前要经过四个步骤——因此“登录”不是一个真正的选项(它打破了网站工作的模式)

    在我的控制器规范中,我能够取消对基于会话变量加载模型的方法的调用——但我不确定“反模式”行是否也适用于存根和模拟


    谢谢

    模拟在黄瓜场景中是不好的——它们几乎是一种反模式

    我的建议是编写一个实际让用户登录的步骤。我是这样做的

    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