16

私はいくつかのステップがある「サインアップ」アプリケーションのためにいくつかのキュウリの物語に取り組んでいます。

それから、すべてのステップを一度にカバーするためにHuuuuuuugeストーリーを書くのではなく、それは悪いことですが、私は通常のユーザーのようにコントローラーの各アクションを実行したいと思います。ここでの私の問題は、最初のステップで作成されたアカウントIDをセッション変数として保存しているため、ステップ2、ステップ3などにアクセスすると、既存の登録データが読み込まれることです。

controller.session[..]RSpec仕様内でアクセスできることは知っていますが、Cucumberストーリーでこれを実行しようとすると、次のエラーで失敗します(また、これはアンチパターンなどです...):

controller.session [:whatever]またはsession [:whatever]を使用する

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

session(:whatever)を使用する

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

したがって、セッションストアへのアクセスは実際には不可能のようです。私が疑問に思っているのは、それが可能かどうかです(そして、どちらが最善かと思います..):

  1. セッションストアなどをモックアウトする
  2. コントローラ内にメソッドを持ち、それをスタブします(たとえばget_registration、インスタンス変数を割り当てます...)

私はRSpecの本を(よく、スキミングして)調べ、WebRatなどを調べましたが、私の問題に対する答えは実際には見つかりませんでした...

もう少し明確にするために、サインアッププロセスはステートマシンのようなものです-たとえば、ユーザーは登録が完了する前に4つのステップを進めます-したがって、「ログイン」は実際にはオプションではありません(サイトの動作モデルを壊します) )..。

コントローラーの仕様では、セッション変数に基づいてモデルをロードするメソッドの呼び出しをスタブ化することができましたが、「アンチパターン」行がモックだけでなくスタブにも適用されるかどうかはわかりませんか?

ありがとう!

4

11 に答える 11

25

キュウリでは可能な限りモックを避けるべきだと言って、ダンピケットを繰り返します。ただし、アプリにログインページがない場合、またはパフォーマンスに問題がある場合は、ログインを直接シミュレートする必要があります。

これは醜いハックですが、仕事を終わらせる必要があります。

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
于 2009-08-18T16:43:59.633 に答える
19

モックはキュウリのシナリオでは良くありません-それらはほとんどアンチパターンのようなものです。

私の提案は、実際にユーザーをログインさせるステップを作成することです。私はこのようにします

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

于 2009-08-13T23:27:15.003 に答える
17

再。Ryanのソリューション-env.rbファイルでActionControllerを開き、そこに配置して、本番コードベースを配置しないようにすることができます(john @ピボットラボに感謝します)

# 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
于 2010-01-15T18:59:04.593 に答える
5

これが元の質問とどの程度関連しているかはもうわかりませんが、とにかく議論の精神で投稿することにしました...

実行に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

私たちにとって、これはcookies配列を操作するよりも簡単になりました。ボーナスとして、このアプローチはSelenium/Watirでも機能します。

欠点は、アプリケーションにテスト関連のコードが含まれていることです。個人的には、アプリケーションをよりテストしやすくするためにコードを追加することは、多少の混乱を加えたとしても、大きな罪ではないと思います。おそらく最大の問題は、将来のテスト作成者が使用するログインの種類を把握する必要があることです。無制限のハードウェアパフォーマンスでは、明らかにこれを実行しません。

于 2009-12-16T14:40:22.593 に答える
4

Re:ライアンの解決策:

小さな適応が行われない限り、カピバラでは機能しません:

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 )

于 2010-12-18T07:56:49.743 に答える
3

私の理解では、次のようになります。

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

リクエストがインスタンス化される前にsession[]にアクセスしたとき。あなたの場合、visit some_existing_pathステップ定義でsession []にアクセスする前にwebratsを配置すると、エラーがなくなると思います。

残念ながら、セッションはステップ間で持続しないようです(少なくとも、私は方法を見つけることができませんでした)ので、この少しの情報はあなたの質問に答えるのに役立ちません:)

だから、私は、ライアンのsession[:user_id] = cookies[:stub_user_id]...ものが行く方法だと思います。ただし、imo、アプリケーション自体のテスト関連コードは正しく聞こえません。

于 2009-09-07T14:23:22.070 に答える
2

Prikkaのようなテスト専用のサインインソリューションを使用していますが、新しいコントローラーとルートを作成する代わりに、すべてを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)
于 2010-03-15T21:41:53.413 に答える
2

@ Ajedi32同じ問題(Capybara :: RackTest ::Driverの未定義のメソッド'current_session')に遭遇し、これをステップ定義に入れると、問題が修正されました。

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_jar [:stub_user_id]ではなくcookies [:stub_user_id]を参照しました

于 2012-09-05T01:51:18.177 に答える
1

FactoryGirlまたは(FixjourまたはFabricator)をDevise(またはAuthlogic)およびSentientUserと一緒に使用してみませんか?次に、どのユーザーがすでにログインしているかを簡単にスニッフィングできます。

@user = Factory(:user)       # FactoryGirl
sign_in @user                # Devise
User.current.should == @user # SentientUser
于 2011-09-08T19:50:13.573 に答える
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
于 2011-01-12T10:35:38.333 に答える
0

たくさんの魂の検索とウェブサーフィンの後、私はついに非常にシンプルで明白な解決策を選びました。

Cookieを使用すると、2つの問題が発生します。まず、テスト専用のコードがアプリケーションにあります。次に、ラックテスト以外のものを使用すると、CucumberでCookieを作成するのが難しいという問題があります。クッキーの問題にはさまざまな解決策がありますが、それらはすべて少し難しいものであり、モックを導入するものもあり、それらはすべて私が「トリッキー」と呼んでいるものです。そのような解決策の1つがここにあります

私の解決策は次のとおりです。これは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を使用しています。

はい…それは少し粗雑ですが、それは明確で、単純で、明白です。そして…私が見た他の解決策は、よりクリーンで明確です。

1つの機能は、user_nameが'testuser'でない場合、テストできるように通常のパスが使用されることです。

これが他の人に役立つことを願っています...

于 2013-03-11T00:26:05.107 に答える