Почему бы не использовать общие соединения ActiveRecord для Rspec + Selenium?

Кажется, наиболее распространенный способ работы с 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

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


person Ryan    schedule 08.01.2012    source источник


Ответы (6)


Это решение было написано Jose Valim — уважаемым в сообществе Rails и членом основной команды Rails. Сомневаюсь, что он порекомендовал бы его использовать, если бы с ним были проблемы. У меня лично проблем не было.

Просто имейте в виду, что если вы используете Spork, это должно быть в блоке each_run для работы.

FWIW - у меня были периодические проблемы с тестом водосвинки с вышеуказанным патчем на Postgres. Решение Майка Перхэма, которое @hsgubert ниже, похоже, решило эти проблемы. Теперь я использую это решение.

person nmott    schedule 08.01.2012
comment
На самом деле это не ответ, а скорее обращение к авторитету. - person Felixyz; 18.07.2014

На самом деле с этим есть проблемы. Например, если вы используете гем 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. Это избавит вас от многих головных болей.

person hsgubert    schedule 23.08.2012
comment
Я все еще получаю ту же ошибку с этим патчем. В блоге предлагается другое решение, но оно не помогите мне тоже. - person Tsutomu; 24.10.2012
comment
В конце концов, причиной моей проблемы является Ajax. Если мы используем этот хак, мы должны дождаться завершения вызова Ajax, прежде чем делать запрос к базе данных. См. замечательную запись в блоге Майка Гехарда Ожидание завершения вызовов jQuery Ajax в Cucumber. - person Tsutomu; 24.10.2012
comment
spacevatican.org/2012/8/18/threading-the-rat объясняет, почему это работает. - person Paul Brannan; 07.01.2013

gem файл readme для DatabaseCleaner отвечает на ваш вопрос "почему бы и нет" следующим образом:

Один из распространенных подходов заключается в том, чтобы заставить все процессы использовать одно и то же соединение с базой данных (общий взлом ActiveRecord), однако сообщалось, что этот подход приводит к недетерминированным сбоям.

person Arcolye    schedule 12.07.2014

Я столкнулся с проблемой при использовании кода, который вы упомянули в моем файле 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.
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>

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

Таким образом, любой тест, который требует перехода ко второму соединению с базой данных для получения информации, завершится ошибкой. :(

Я собираюсь опубликовать эту проблему и посмотреть, нашел ли кто-нибудь решение.

person Mieczysław Daniel Dyba    schedule 26.07.2013
comment
Когда я вставил этот код в свой ruby-скрипт, я получил вот это. ruby(43221,0x109043000) malloc: *** ошибка для объекта 0x7f915136e8c0: освобождаемый указатель не был выделен Я бы посчитал это недостатком. - person baash05; 04.12.2013

Я просто немного читал об этом сам. Я обнаружил фрагмент, которым вы поделились здесь, в этом сообщении блога:

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

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

person counterbeing    schedule 04.08.2013

В конце этого поста есть хорошая вещь. Это может объяснить, почему я получаю сообщение об ошибке 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)
person baash05    schedule 04.12.2013