testing and establish_connection
Asked Answered
K

3

5

How do I write tests for projects which use establish_connection in model to connect to another database?

Kedge answered 30/11, 2009 at 8:54 Comment(0)
S
10

When you do establish_connection for certain models to connect to a different database, one of the problem you would face while testing those tables is that the test data you created wont be rolled back automatically.

The actual code to create a transaction save point and to rollback the data for test lives in rails/activerecord/lib/active_record/fixtures.rb. And especially there are two methods setup_fixtures and teardown_fixtures. The code in those methods are straight forward. They just create a savepoint and does rollback for each test. But it does only for the ActiveRecord::Base connection.

So what you have to do is "monkey patch" these methods so that, in addition to the ActiveRecord::Base connection, the same set of operations are done for your additional database connection.

Here is a sample code for the same:

## database.yml
development:
  database: dev
test:
  database: test
#...
my_connection_development:
  database: my_connection_dev
my_connection_test:
  database: my_connection_test
#...

## my_connection_base.rb
class MyConnectionBase < ActiveRecord::Base
  establish_connection(ActiveRecord::Base.configurations["my_connection_#{RAILS_ENV}"])
  self.abstract_class = true
end

## my_model.rb
class MyModel < MyConnectionBase
end

## my_another_model.rb
class MyAnotherModel < MyConnectionBase
end

## test_case_patch.rb
module ActiveSupport
  class TestCase
    def setup_fixtures
      return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
      if pre_loaded_fixtures && !use_transactional_fixtures
        raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
      end
      @fixture_cache = {}
      @@already_loaded_fixtures ||= {}
      # Load fixtures once and begin transaction.
      if run_in_transaction?
        if @@already_loaded_fixtures[self.class]
          @loaded_fixtures = @@already_loaded_fixtures[self.class]
        else
          load_fixtures
          @@already_loaded_fixtures[self.class] = @loaded_fixtures
        end

        ActiveRecord::Base.connection.increment_open_transactions
        ActiveRecord::Base.connection.transaction_joinable = false
        ActiveRecord::Base.connection.begin_db_transaction

        MyConnectionBase.connection.increment_open_transactions
        MyConnectionBase.connection.transaction_joinable = false
        MyConnectionBase.connection.begin_db_transaction
      # Load fixtures for every test.
      else
        Fixtures.reset_cache
        @@already_loaded_fixtures[self.class] = nil
        load_fixtures
      end
      # Instantiate fixtures for every test if requested.
      instantiate_fixtures if use_instantiated_fixtures
    end

    def teardown_fixtures
      return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
      unless run_in_transaction?
        Fixtures.reset_cache
      end
      # Rollback changes if a transaction is active.
      if run_in_transaction? && MyConnectionBase.connection.open_transactions != 0
        MyConnectionBase.connection.rollback_db_transaction
        MyConnectionBase.connection.decrement_open_transactions
      end
      # Rollback changes if a transaction is active.
      if run_in_transaction? && ActiveRecord::Base.connection.open_transactions != 0
        ActiveRecord::Base.connection.rollback_db_transaction
        ActiveRecord::Base.connection.decrement_open_transactions
      end
      MyConnectionBase.clear_active_connections!
      ActiveRecord::Base.clear_active_connections!
    end
  end
end
Shena answered 30/11, 2009 at 12:44 Comment(2)
This is working for me in Rails 2.3.5 but only if I create and populate the remote models in the test code. If I try to use fixtures, load_fixtures looks for the table in the local db rather than the remote db. Is there another step to allow fixtures for models using establish_connection? I don't have to use fixtures, but was curious to learn.Gentleman
@Anatortoise House: Honestly speaking, I hate fixtures and have never tried this with fixtures. Afaik, fixtures are loaded before the test suite begins without any transaction and hence at each test, if you happen to delete some data(wrapped in a transaction) created by fixture, it will be rolled back at the end of the transaction. So this should work automatically.Shena
N
0

I don't see a need to change the test codes after you place establish_connection in your model since you still test the same model functionality.

Nomanomad answered 30/11, 2009 at 9:17 Comment(0)
W
-2

You will not need to Test the establish_connection method, as its a activrecord method and is well tested, before release.

although if you still want to do so, call the method within a different method, and see if you can connect to appropriate tables in that DB.

Wilber answered 30/11, 2009 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.