Why not use shared ActiveRecord connections for Rspec + Selenium?
It seems the most commonly accepted way to deal with Selenium and tests is to avoid using transactional fixtures and then using something like database_cleaner between tests/scenarios. I recently ran into the following article which suggested doing the following:
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
This seems loads better for performance than the alternatives. Does anyone have any reason why this shouldn't be used?
Solution 1:
Actually there are issues with it. If you use the gem mysql2, for example, you'll start seeing some errors like:
Mysql2::Error This connection is still waiting for a result
Please use this instead. It was written by Mike Perham, all credits to him.
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
You'll need to install gem connection_pool
too.
This will spare you from many headaches.
Solution 2:
This solution was written by Jose Valim - well respected in the Rails community and a member of the Rails core team. I doubt he would recommend using it if there were issues with it. I personally haven't had any issues.
Just be aware that if you use Spork this needs to be in the each_run block to work.
FWIW - I have had intermittent capybara test issues with the above patch on Postgres. The Mike Perham solution that @hsgubert has below appears to have solved those issues. I am now use that solution.
Solution 3:
The DatabaseCleaner gem readme answers your "why not" question this way:
One common approach is to force all processes to use the same database connection (common ActiveRecord hack) however this approach has been reported to result in non-deterministic failures.
Solution 4:
I have encountered a problem using the code you mentioned in my spec_helper.rb file.
What happens when your tests depend on using connections to multiple databases? I have two databases I need to connect to when I run my tests. I did a simple test to check what was happening to the database connections I establish.
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>
As you can see, before I call the shared connection method, I have two different database connections. After, the shared connection method call, I have only one.
So any test that requires going to the second database connection to retrieve information will fail. :(
I'm going to post this problem and see if anyone has arrived at a solution.