web-dev-qa-db-ja.com

Rspec + Seleniumに共有ActiveRecord接続を使用してみませんか?

Seleniumとテストを処理するための最も一般的に受け入れられている方法は、トランザクションフィクスチャの使用を避け、テスト/シナリオ間でdatabase_cleanerのようなものを使用することです。私は最近次のことに遭遇しました 記事 これは次のことを提案しました:

spec_helper.rb

class ActiveRecord::Base
  mattr_accessor :shared_connection
  @@shared_connection = nil

  def self.connection
    @@shared_connection || retrieve_connection
  end
end

# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

これは、他の方法よりもパフォーマンスの点で負荷が高いようです。これを使用すべきでない理由はありますか?

46
Ryan

このソリューションは、JoseValimによって作成されました。Railsコミュニティで高く評価されており、Railsコアチームのメンバーです。個人的には何の問題もありませんでした。

Sporkを使用する場合、これが機能するには、each_runブロックにある必要があることに注意してください。

FWIW-Postgresの上記のパッチで断続的なcapybaraテストの問題が発生しました。 @hsgubertが以下に持っているMikePerhamソリューションは、これらの問題を解決したようです。私は今そのソリューションを使用しています。

15
nmott

実際には問題があります。たとえば、gem mysql2を使用すると、次のようなエラーが発生し始めます。

Mysql2::Error This connection is still waiting for a result

代わりにこれを使用してください。それはマイク・パーハムによって書かれました、彼へのすべてのクレジット。

class ActiveRecord::Base
  mattr_accessor :shared_connection
  @@shared_connection = nil

  def self.connection
    @@shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
  end
end

ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

Gem connection_poolもインストールする必要があります。これにより、多くの頭痛の種からあなたを救うことができます。

33
hsgubert

DatabaseCleaner gem readmeは、「なぜ」という質問に次のように答えます。

一般的なアプローチの1つは、すべてのプロセスに同じデータベース接続を使用させることです( 一般的なActiveRecordハック )が、このアプローチは非決定論的な失敗をもたらすことが報告されています。

2
Arcolye

Spec_helper.rbファイルに記載されているコードを使用して問題が発生しました。

テストが複数のデータベースへの接続の使用に依存している場合はどうなりますか?テストを実行するときに接続する必要のあるデータベースが2つあります。確立したデータベース接続に何が起こっているかを確認するために、簡単なテストを行いました。

class ActiveRecord::Base
   mattr_accessor :shared_connection
   @@shared_connection = nil

   def self.connection
     @@shared_connection || retrieve_connection
  end
end

# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
puts "First Record cxn: #{FirstDatabase::Record.connection}"
# => First Record cxn: #<ActiveRecord::ConnectionAdapters::Mysql2Adapter:0xe59b524>
puts "AR Base cxn: #{ActiveRecord::Base.connection}"
# => AR Base cxn: #<ActiveRecord::ConnectionAdapters::Mysql2Adapter:0xc52761c>
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

puts "First Record cxn: #{FirstDatabase::Record.connection}"
# => First Record cxn: #<ActiveRecord::ConnectionAdapters::Mysql2Adapter:0xc52761c>
puts "AR Base cxn: #{ActiveRecord::Base.connection}"
# => AR Base cxn: #<ActiveRecord::ConnectionAdapters::Mysql2Adapter:0xc52761c>

ご覧のとおり、共有接続メソッドを呼び出す前に、2つの異なるデータベース接続があります。共有接続メソッドを呼び出した後、1つしかありません。

したがって、情報を取得するために2番目のデータベース接続に移動する必要があるテストは失敗します。 :(

この問題を投稿して、誰かが解決策に到達したかどうかを確認します。

この投稿の最後に良いことがあります。非常に単純なスレッドスクリプトを実行しようとすると、MALLOCエラーが発生する理由が説明される場合があります。

http://apidock.com/Rails/ActiveRecord/Base/connection

leente - March 15, 2011 0 thanks
Don't cache it!

Don’t store a connection in a variable, because another thread might try to use it when it’s already checked back in into the connection pool. See: ActiveRecord::ConnectionAdapters::ConnectionPool

connection = ActiveRecord::Base.connection

threads = (1..100).map do
  Thread.new do
    begin
      10.times do
        connection.execute("SELECT SLEEP(1)")  # WRONG
        ActiveRecord::Base.connection.execute("SELECT SLEEP(1)")  # CORRECT
      end
      puts "success"
    rescue => e
      puts e.message
    end
  end
end

threads.each(&:join)
1
baash05

私はこれについて少し読んでいました。このブログ投稿であなたがここで共有したスニペットを発見しました:

http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite/

あなたの質問に直接答えるために、 データベースクリーナーgithubページ は「非決定論的な失敗につながる」可能性があることを警告しています。私はすぐにそれを使用しますが、あなたが奇妙な失敗に遭遇し始めたら、多分これは探し始めるのに良い場所です。

1
counterbeing