14

RSpec と Capybara を使用すると、興味深いテスト失敗モードが得られます。これは、テスト ケース内の行のいくつかの微妙な再配置で解消されます...問題にならないものです。

独自の認証システムを開発しています。現在動作しており、ブラウザでログイン/アウトでき、セッションも動作します。ただし、これをテストしようとすると失敗します。(一見) 無関係な呼び出しの順序に依存しているように見える、よく理解できないことが起こっています。

require 'spec_helper'

describe "Sessions" do
  it 'allows user to login' do
    #line one
    user = Factory(:user)
    #For SO, this method hashes the input password and saves the record
    user.password! '2468'

    #line two
    visit '/sessions/index'


    fill_in 'Email', :with => user.email
    fill_in 'Password', :with => '2468'
    click_button 'Sign in'

    page.should have_content('Logged in')
  end
end

そのままでは、そのテストは失敗します...ログインは失敗します。スペックとコントローラーの両方に「デバッガー」呼び出しを挿入した後、理由がわかります。コントローラーに関する限り、ユーザーはデータベースに挿入されていません。

ApplicationController に追加する編集

class ApplicationController < ActionController::Base
  helper :all
  protect_from_forgery

  helper_method :user_signed_in?, :guest_user?, :current_user

  def user_signed_in?
    !(session[:user_id].nil? || current_user.new_record?)
  end

  def guest_user?
    current_user.new_record?
  end

  def current_user
    @current_user ||= session[:user_id].nil? ? User.new : User.find(session[:user_id])
  rescue ActiveRecord::RecordNotFound
    @current_user = User.new
    flash[:notice] = 'You\'ve been logged out.'
  end
end


class SessionsController < ApplicationController
  def login
    user = User.where(:email=>params[:user][:email]).first

    debugger ###

    if !user.nil? && user.valid_password?(params[:user][:password])
      #engage session
    else
      #run away
    end
  end

  def logout
    reset_session
    redirect_to root_path, :notice => 'Logget Out.'
  end
end

コンソールの上記のブレークポイントで:

1.9.2 vox@Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb 
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
 => 0 
ruby-1.9.2-p180 :002 > 

ただし、テストで数行を並べ替えて、「2」行を「1」行の上に配置すると、次のようになります。

describe "Sessions" do
  it 'allows user to login' do
    #line two
    visit '/sessions/index'

    #line one
    user = Factory(:user)
    #For SO, this method hashes the input password and saves the record
    user.password! '2468'


    fill_in 'Email', :with => user.email
    fill_in 'Password', :with => '2468'
    click_button 'Sign in'

    page.should have_content('Logged in')
  end
end

コンソールでこれを取得します(上記と同じブレークポイント):

1.9.2 vox@Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb 
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
 => 1 

簡潔にするために、ユーザー オブジェクトのコンテンツの完全なダンプは省略しましたが、テストが期待どおりに完了することは保証できます。

テストに合格するために行を交換するというこの動作は、これらのコマンドで何が行われるべきかについての私の考えとはあまり一致せず、他の領域での私のテストにとってかなりの負担になることが証明されています。

ここで何が起こっているかについてのヒントはありますか?

この問題を提示するアイデアについて Google と SO を精査しましたが、RSpec/Capybara と Sessions に関する SO の質問に事欠きません。しかし、何もぴったりとは思えませんでした。

ご覧いただきありがとうございます。

アップデート

ブレークポイント (visit 呼び出しの直前) といくつかのデバッグをテストに追加し、次のように戻ってきました。

(rdb:1) user
#<User id: 1, login_count: 1, email: "testuser1@website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">
(rdb:1) User.all
[#<User id: 1, login_count: 1, email: "testuser1@website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">]
(rdb:1) next
/Users/vox/Sites/website/spec/controllers/sessions_controller_spec.rb:19
fill_in 'Email', :with => user.email
(rdb:1) User.all
[]

明らかに、visit の途中で何かが Factory Girl にユーザー オブジェクトの処理が完了したことを伝え、それで彼女はそれを削除するのでしょうか?

編集test.log を注意深く調べた後、何も削除を発行していません。だから私は多かれ少なかれ振り出しに戻っています。

4

4 に答える 4

23

Factory Girl メーリング リストの助けを借りて、この問題を発見しました。

デフォルトでは、RSpec はトランザクションを使用してデータベースをクリーンな状態に維持し、各トランザクションはスレッドに関連付けられています。パイプラインのどこかで、visit_page コマンドが分割され、現在のスレッドに関連付けられているトランザクションが終了します。

解決策は簡単です。トランザクションを無効にします。

describe "Sessions" do
  self.use_transactional_fixtures = false

   it 'no longer uses transactions' do
     #whatever you want
  end
end

Rails 5.1 のアップデート

Rails 5.1 以降、use_transactional_fixturesは非推奨となり、 に置き換える必要がありuse_transactional_testsます。

self.use_transactional_tests = false
于 2011-08-18T19:10:45.943 に答える
3

RSpecのユーザー変数がコントローラーのユーザー変数を上書きしたため、機能しなかったと思いますか?(テストで正しいuser.emailを取得できませんでした)

前 :

user = Factory(:user)
user.password! '2468'

visit '/sessions/index' # user gets overwritten

fill_in 'Email', :with => user.email # can't get user.email

後 :

visit '/sessions/index' # Execute action

user = Factory(:user) # user gets overwritten
user.password! '2468'

fill_in 'Email', :with => user.email  # user.email works
于 2011-08-16T22:54:03.537 に答える
1

これは技術的には答えではなく、コメントですが、コードを明確にするのが最も簡単なメカニズムです。

ユーザーが破壊されている場所を絞り込むために、次のことを試してみてください。

describe "Sessions" do
  it 'allows user to login' do
    #line one
    user = Factory(:user)
    #For SO, this method hashes the input password and saves the record
    user.password! '2468'


# check the user's definitely there before page load
puts User.first

    #line two
    visit '/sessions/index'

# check the user's still there after page load
puts User.first.reload


    fill_in 'Email', :with => user.email
    fill_in 'Password', :with => '2468'
    click_button 'Sign in'

# check the user's still there on submission (though evidently not)
puts User.first.reload

    page.should have_content('Logged in')
  end
end

編集

実生活では問題なく機能しますが、カピバラでは機能しないという事実は、既存のセッション情報の産物である可能性があることを示唆しています. ブラウザでテストしているときは、通常、前の作業の後ろに進んでいますが、Capybara は常にクリーンなセッションから開始します。

すべての Cookie をクリアするか (ご存じのとおり)、Chrome/FF の新しいシークレット ウィンドウに切り替えるだけで、ブラウザーでカピバラ エラーを再現できるかどうかを簡単に確認できます。クリーンセッション。

于 2011-08-16T23:13:50.040 に答える
0

上記の正しい答えは私を助けました。もちろん、フィクスチャが存在しないと想定していた (間違っていても正しくても) 他のいくつかのテストを変更する必要がありました。詳細については、これに関する情報が Capybara README にあります。

https://github.com/jnicklas/capybara

「SQLデータベースを使用している場合、すべてのテストをトランザクションで実行するのが一般的です。これはテストの最後にロールバックされます。たとえば、rspec-railsはデフォルトでこれを行います。トランザクションは通常そうではないためスレッド間で共有すると、テストコードでデータベースに入れたデータが Capybara から見えなくなります。」

テスト後に手動でクリーンアップするように RSpec を構成することもできます。

https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Cleanup-after-your-Rspec-tests

于 2013-04-15T01:15:13.893 に答える