2009-08-13 6 views
16

많은 단계가있는 '가입'애플리케이션에 대한 일부 오이 이야기를하고 있습니다.Cucumber Stories가있는 세션 변수

오히려 일 것입니다 모든 단계를 즉시 커버 Huuuuuuuuge 이야기를 쓰고, 나는 오히려 일반 사용자처럼 컨트롤러의 각 작업을 통해 일하고 싶습니다. 내 문제는 여기에 세션 변수로 첫 번째 단계에서 생성 된 계정 ID를 저장하고 있으므로 2 단계, 3 단계 등을 방문하면 기존 등록 데이터가로드됩니다.

RSpec 사양에서 controller.session[..]에 액세스 할 수 있다는 것을 알고 있지만 Cucumber 스토리에서 다음과 같은 오류가 발생하면이 오류가 발생합니다. (또한 어딘가에 읽었을 때 안티 패턴 등입니다. ..) : : 어떤] 또는 세션 [: 어떤]

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

을 세션 (사용 : controller.session의 사용

이든)

그래서 세션 저장소의 가입이 실제로 가능하지 않은 것처럼 보입니다. 내가 궁금하면 (.. 내가 가장 잘 될 것이라고 생각)이 가능한있을 경우입니다 :

  1. 모의 아웃 세션 저장소 등
  2. 컨트롤러 내에서 방법을 가지고 있고 (그 밖으로 스텁 나는 RSpec에 책을 보았다 (물론, 무 지방) 등 WebRat을 통해보고했지만, 난 정말 내 문제에 대한 답변을 못 찾았습니다

인스턴스 변수를 할당 예를 들어 get_registration ...) ...

좀 더 명확히하기 위해 가입 프로세스는 상태 머신처럼 - 예 : 사용자가 등록이 완료되기 전에 네 단계를 거쳐 진행되므로 '로그인'은 실제로 옵션이 아닙니다 (사이트 작동 방식의 모델이 깨집니다).

컨트롤러 사양 세션 var에 따라 모델을로드하는 메소드에 대한 호출을 스텁 (stub)합니다. 그러나 '안티 패턴'라인이 mock뿐 아니라 스텁에도 적용되는지 확실하지 않습니다.

감사합니다.

답변

19

오이 시나리오에서는 조롱이 좋지 않습니다. 거의 반 패턴입니다.

나의 제안은 내가

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이라고 부릅니다.

+1

덕분에 ... 좀 더 세부 내 질문에 업데이 트했습니다,하지만이 기능을 추가 포함하는 것처럼 정말 위해 특별히 당신이 제안을 도움이되지 않습니다 나에게 '안티 패턴 (anti-pattern)'처럼 보이지만, 컨트롤러 메소드를 스텁 (stub)하면이 규칙을 어기는 테스트가 필요하다. –

+1

추가 기능? 사용자가 귀하의 사이트에 어떻게 로그인합니까? 등록 프로세스는 언급했지만 로그인은 언급하지 않았습니다. 위에 표시된 것처럼 이미 등록 된 사용자의 공장을 만들고 로그인 할 수없는 이유는 무엇입니까? 오이 이야기는 사용자가 귀하의 사이트와 상호 작용하는 방식을 정확하게 표현해야합니다. 사용자는 컨트롤러 메소드를 스텁 아웃 할 수 없습니다. 작성 양식과 클릭 연결 링크가 있습니다. 사용자 행동을 모방하지 않으면 수용 테스트가 아닙니다. – danpickett

+0

여기 양쪽에 있습니다.필자는 인증 테스트가 원하는 방식으로 작동하는 기능에 대해 위와 거의 똑같은 기능을 제공합니다. 다른 기능의 경우, 모든 카피 바라 단계를 반복해야 모든 시나리오에 로그인 할 수 있습니다. 오이는 테스트 목적으로 데이터베이스 상태를 조작 할 수 있습니다. 왜 세션이 아닌가? –

24

오이에서 가능할 때마다 mock을 피해야한다고 말하는 데 danpickett를 반복하겠습니다. 그러나 앱에 로그인 페이지가 없거나 성능에 문제가있는 경우 로그인을 직접 시뮬레이션해야 할 수도 있습니다.

이것은 못생긴 해킹이지만 일을 끝내야합니다.

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 
+0

OAuth 사이트의 로그인을 건너 뛰기 위해이 기능을 사용했습니다. 훌륭한. Ryan에게 감사드립니다. –

3

나의 이해는 당신이 얻을 것입니다 :

요청이 인스턴스화되기 전에 세션이 []에 액세스
You have a nil object when you didn't expect it! 
The error occurred while evaluating nil.session (NoMethodError) 

. 당신의 단계 defenition에서 세션 []에 액세스하기 전에 webrats 'visit some_existing_path을 넣으면 오류가 사라질 것이라고 생각합니다.

이제

, 불행하게도, 세션 단계 (적어도 내가 방법을 찾을 수 없습니다)에 걸쳐 지속하지 않는 것, 그래서 정보의 비트 질문 : 그래서

에 대답하는 데 도움이되지 않습니다 , 제 생각 엔 라이언의 session[:user_id] = cookies[:stub_user_id]... 가야 할 길입니다. 하지만, 응용 프로그램 자체의 테스트 관련 코드가 제대로 작동하지 않더라도. 우리는 실행> 10 분 소요 오이 테스트 스위트를

5

나는이 더 이상 원래의 질문에 관련이 얼마나 모르겠지만, 나는 토론의 정신 어쨌든 게시하기로 결정 ... 그래서 우리는 최적화를 원했습니다. 우리의 앱에서 로그인 프로세스는 대다수 시나리오와 관련이없는 추가 기능을 많이 트리거하므로 세션 사용자 ID를 직접 설정하여 건너 뛰기를 원했습니다.

위의 Ryanb의 접근 방식은 그 접근 방식을 사용하여 로그 아웃 할 수 없다는 점을 제외하면 훌륭하게 작동했습니다. 이로 인해 다중 사용자 스토리가 실패했습니다. 우리이를 위해

# 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 routes.rb 
map.connect '/quick_login/:login', :controller => 'logins', :action => 'quick_login' 

세션 변수를 만들고 해당 작업입니다 :

우리는 테스트 환경에서 사용 가능 "빠른 로그인"경로를 만들어 결국 쿠키 배열 작업보다 간단 해졌습니다. 보너스로이 접근법은 Selenium/Watir에서도 작동합니다.

단점은 테스트 관련 코드가 애플리케이션에 포함되어 있다는 점입니다. 개인적으로 나는 응용 프로그램을 더 쉽게 테스트 할 수 있도록 코드를 추가하는 것이 약간의 혼란을 일으키더라도 커다란 죄라고 생각하지 않습니다. 가장 큰 문제는 미래의 테스트 작성자가 사용해야하는 로그인 유형을 알아 내야한다는 것입니다. 무제한의 하드웨어 성능으로 우리는 분명히이 일을하지 않을 것입니다.

+0

흥미로운 아이디어, 그리고 테스트 코드로 프로덕션 준비 코드를 대체 할 수있는 대안이있을 수 있습니다 - 테스트가 시작될 때 새로운 경로를 삽입하고 quicklogin 코드를 처리하기 위해 응용 프로그램 컨트롤러 class_eval을 주입 할 수 있습니다 ... 그냥 어쨌든 생각;) 귀하의 기여에 감사드립니다! –

16

Re. 라이언의 솔루션 - 당신은 당신이 파일을 env.rb에 ActionController를 열고 생산 코드베이스에 넣어 피하기 위해 거기에 배치 할 수 있습니다 (덕분에 존 @ 중요한 실험실)

# 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 
+0

필자는 레일 4에 있는데, 기존의'before_filter' 함수가 "방지"되고,'helper_method'가 존재하지 않게하는 것을 관찰하고 있습니다. – Narfanator

2

내가 사용하는 테스트 전용 로그인에 Prikka's과 같은 솔루션이지만 새로운 컨트롤러와 라우트를 만드는 대신 랙에서 모든 작업을 수행합니다.

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

재 : 라이언의 솔루션 :

작은 적응 수행하지 않는 한, 카피 바라 작동하지 않습니다

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)

+0

다시 솔루션을 제공해 주셔서 감사합니다. 많은 도움이되었습니다. 매력처럼 작동합니다. – Pedro

+1

나에게있어 오류가 발생했다. # 에 대한 '정의되지 않은 메소드'current_session '애플리케이션에서이 코드를 어디에 넣어야하는지 구체적으로 설명해 주시겠습니까? – Ajedi32

0

또 다른 약간의 변화 :

# 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 
+1

이 오류가 발생합니다. ' (NoMethodError)'에 대해 정의되지 않은 메소드 'post'. 적어도 우리는 셀레늄으로 올릴 수없는 것 같습니다. –

1

FactoryGirl 또는 (Fixjour 또는 Fabricator)를 Devise (또는 Authlogic) 및 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 

:

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

는 Ajedi32 @ 나를 위해 문제를 해결 같은 (카피 바라 :: RackTest에 대한 정의되지 않은 메서드 'current_session':: 드라이버) 문제와 나의 단계 정의에서이 퍼팅에 달렸다 내 컨트롤러 작업에서 cookie_jar [: stub_user_id] 대신에 쿠키 [: stub_user_id]를 참조했습니다.

0

많은 영혼을 검색하고 웹 서핑을 한 후에 마침내 매우 간단하고 분명한 해결책을 선택했습니다.

쿠키를 사용하면 두 가지 문제점이 발생합니다. 먼저 응용 프로그램에 테스트 용 코드가 있고 두 번째로 랙 테스트 이외의 다른 것을 사용할 때 Cucumber에서 쿠키를 만드는 것이 어렵다는 문제가 있습니다. 쿠키 문제에 대한 여러 가지 해결책이 있지만 모두 약간 도전적이며, 일부는 가짜를 소개하며, 모두 내가 까다로운 내용입니다. 그러한 해결책 중 하나는 here입니다.

내 솔루션은 다음과 같습니다. 이것은 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를 사용하여 피하고 싶었습니다.

예 ... 다소 번거롭지만 분명하고 간단하며 명백합니다. 그리고 ... 내가 본 다른 해결책은 더 깨끗하고 분명하지 않다.

user_name이 'testuser'가 아닌 경우 테스트 할 수 있도록 정상 경로가 사용된다는 점에 유의하십시오. 이 다른 사람을 도움이

희망 ... 입력에 대한