Подтвердить что ты не робот

Переменные сеанса с огурцами

Я работаю над некоторыми историями огурца для приложения "зарегистрироваться", которое имеет несколько шагов.

Вместо того, чтобы писать историю Huuuuuuuge, чтобы охватить все шаги сразу, что было бы плохо, я предпочел бы работать через каждое действие в контроллере, как обычный пользователь. Моя проблема заключается в том, что я храню идентификатор учетной записи, который создается на первом шаге в качестве переменной сеанса, поэтому, когда посетитель переходит к шагу 2, шаг 3 и т.д., Загружаются существующие данные регистрации.

Мне известно, что я могу получить доступ к controller.session[..] в спецификациях RSpec, однако, когда я пытаюсь сделать это в истории огурца, он терпит неудачу со следующей ошибкой (и, я также читал, что это анти-шаблон и т.д....):

Использование controller.session [: whatever] или session [: whatever]

You have a nil object when you didn't expect it!
The error occurred while evaluating nil.session (NoMethodError)

Использование сеанса (: независимо)

wrong number of arguments (1 for 0) (ArgumentError)

Итак, похоже, что сеансовый магазин невозможен. Мне интересно, возможно ли это (и я думаю, что было бы лучше...):

  • Выделите хранилище сеансов и т.д.
  • Имейте метод внутри контроллера и заглушите его (например, get_registration, который присваивает переменную экземпляра...)

Я просмотрел книгу RSpec (ну, снял) и просмотрел WebRat и т.д., но я не нашел ответа на свою проблему...

Чтобы уточнить немного больше, процесс регистрации больше похож на конечный автомат - например, пользователь проходит через четыре шага до завершения регистрации - поэтому "вход в систему" ​​на самом деле не является вариантом (он нарушает модель работы сайта)...

В моем спецификации для контроллера я смог заглушить вызов метода, который загружает модель на основе сеанса var - но я не уверен, что линия "antipattern" также применяется к окуркам, а также к mocks

Спасибо!

4b9b3361

Ответ 1

В сценариях огурцов плохое издевательство - они почти вроде антипаттера.

Мое предложение - написать шаг, который фактически регистрирует пользователя. Я делаю это таким образом

Given I am logged in as "[email protected]"

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.

Ответ 2

Я повторю, что данпикетт говорит, что в Огуреме следует избегать издевательств. Однако, если ваше приложение не имеет страницы входа в систему, или, возможно, производительность является проблемой, тогда может возникнуть необходимость имитировать вход в систему напрямую.

Это уродливый взлом, но он должен выполнить свою работу.

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

Ответ 3

Re. Ryan - вы можете открыть ActionController в вашем файле env.rb и поместить его туда, чтобы не вводить свою производственную базу кода (благодаря john @pivotal labs).

# 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

Ответ 4

Я не знаю, насколько это больше относится к исходному вопросу, но я решил выступить в духе обсуждения...

У нас есть набор тестов огурца, который длится > 10 минут, поэтому мы хотели бы сделать некоторую оптимизацию. В нашем приложении процесс входа в систему вызывает много дополнительных функций, которые не имеют отношения к большинству сценариев, поэтому мы хотели пропустить это, установив непосредственно идентификатор пользователя сеанса.

Подход 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 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

Для нас это оказалось проще, чем работа с массивом файлов cookie. В качестве бонуса этот подход также работает с Selenium/Watir.

Даунсайд - это то, что мы включаем тестовый код в нашем приложении. Лично я не думаю, что добавление кода, чтобы сделать приложение более тестируемым, - огромный грех, даже если он добавляет немного беспорядка. Возможно, самая большая проблема заключается в том, что будущим авторам испытаний необходимо выяснить, какой тип входа они должны использовать. С неограниченной производительностью оборудования мы, очевидно, не будем делать ничего из этого.

Ответ 5

Re: Раян решение:

Не работает с Capybara, если не сделано небольшой адаптации:

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

(найдено здесь: https://gist.github.com/484787)

Ответ 6

Я понимаю, что вы получаете:

You have a nil object when you didn't expect it!
The error occurred while evaluating nil.session (NoMethodError)

когда сеанс [] доступен, прежде чем запрос был создан. В вашем случае я бы предположил, что если вы поместите webrats visit some_existing_path перед тем, как перейти к сеансу [] в своем шаге defenition, ошибка исчезнет.

Теперь, к сожалению, сеанс, похоже, не сохраняется на всех этапах (по крайней мере, я не мог найти пути), поэтому этот бит информации не помогает ответить на ваш вопрос:)

Итак, я полагаю, Райан session[:user_id] = cookies[:stub_user_id]... - это путь. Хотя, imo, связанный с тестированием код в самом приложении не звучит правильно.

Ответ 7

Я использую только вход для проверки, например Прикки, но я делаю все это в Rack вместо создания нового контроллера и маршрутов.

# 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)

Ответ 8

@Ajedi32 Я столкнулся с той же проблемой (undefined method 'current_session' для Capybara:: RackTest:: Driver), и, поставив это в моем определении шага, я решил проблему для меня:

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

В моем действии контроллера я упомянул файлы cookie [: stub_user_id], а не cookie_jar [: stub_user_id]

Ответ 9

Почему бы вам не использовать FactoryGirl или (Fixjour или Fabricator) с Devise (или Authlogic) и SentientUser? Тогда вы можете просто понюхать, какой пользователь уже зарегистрировался!

@user = Factory(:user)       # FactoryGirl
sign_in @user                # Devise
User.current.should == @user # SentientUser

Ответ 10

Еще одно небольшое изменение:

# 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

Ответ 11

После большого поиска души и веб-серфинга я, наконец, выбрал очень простое и очевидное решение.

Использование файлов cookie добавляет две проблемы. Сначала у вас есть код в приложении, предназначенном для тестирования, а во-вторых, проблема с созданием файлов cookie в Cucumber затруднена при использовании чего-либо другого, кроме проверки стойки. Существуют различные решения проблемы cookie, но все они немного сложны, некоторые вводят насмешки, и все они являются тем, что я называю "сложным". Одним из таких решений является здесь.

Мое решение следующее. Это использует базовую аутентификацию HTTP, но для большинства может быть обобщено.

  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

test_authenticate делает то, что делает обычный аутентификатор, за исключением того, что он обходит любые трудоемкие части. В моем случае подлинная аутентификация использует LDAP, которого я хотел избежать.

Да... это немного грубо, но ясно, просто и очевидно. И... никакое другое решение, которое я видел, не является более чистым или понятным.

Примечание. Одна особенность заключается в том, что если имя_пользователя не является "тестостером", тогда выполняется нормальный путь, чтобы их можно было протестировать.

Надеюсь, это поможет другим...