4

I have a configuration where, in addition to the local postgresql database, my Rails app also accesses a remote AWS database. My problem is that, even in tests that don't involve the remote database, the app establishes a connection to the remote database every time, so my tests run sloooooowly.

Is there a clean way to disable access to the remote database server for rspec tests that don't need it? (And enable it for those tests that do need it?)

The best I can think of is partition my rspec tests into two separate parts -- those that don't need to access the remote db and those that do -- and use environment variables to enable or disable parts of config/database.yaml accordingly.

To make this clear, my config/database.yaml file contains (in part):

# file: config/database.yaml

# Define connections to the external database
remote:
  adapter: mysql
  database: remote
  username: <%= ENV['PRODUCTION_DB_USERNAME'] || 'root' %>
  password: <%= ENV['PRODUCTION_DB_PASSWORD'] || '' %>
  host: awsserver-production-mysql.abcdef1234567890.us-west-2.rds.amazonaws.com
  port: 3306

test:
  adapter: postgresql
  encoding: unicode
  database: MyApp_test
  pool: 5
  username: <%= ENV['POSTGRESQL_DB_USERNAME'] || 'MyApp' %>
  password: <%= ENV['POSTGRESQL_DB_PASSWORD'] || '' %>

(NOTE: In case you're wondering, using mocks won't help: the remote db connection is established even before the tests start to run. And vcr only intercepts HTTP connections -- database connections use a different mechanism.)

update

I've found examples of how to dynamically establish a connection:

def connect_to_myapp_database
  ActiveRecord::Base.establish_connection(:adapter => "mysql",
                                          :database => 'myapp',
                                          :username => ENV['MYAPP_DB_USERNAME'],
                                          :password => ENV['MYAPP_DB_PASSWORD'],
                                          :host => 'mayapp-mysql.abcdefg123456.us-west-2.rds.amazonaws.com',
                                          :port => 3306,
                                          )
end

which works fine -- I can use this to connect to the external database just for those tests that need it. But this begs the question: how do I disconnect from the external database once I've done this?

fearless_fool
  • 33,645
  • 23
  • 135
  • 217

1 Answers1

3

TL;DR: Use nulldb and a test-aware parent class

Use nulldb when you're testing and the real db otherwise. Here's how:

First, include this in your Gemfile:

group :development, :test do
  gem 'activerecord-nulldb-adapter', :git => 'git://github.com/nulldb/nulldb.git'
end

and then do the usual bundle install

Define a base class for all models that are backed in the external database:

class ExternalModel < ActiveRecord::Base
  if Rails.app.test?
    establish_connection(:adapter => :nulldb)
  else
    establish_connection(:myapp)
  end

  def readonly?; true; end
end

Then all the external models inherit from ExternalModel (I should have done this from the start):

class ExternalUser < ExternalModel
  ...
end

When run in a test environment, it won't try to connect to the external table. Of course, attempts to access an instance of ExternalUser will fail, but you can selectively establish a connection with the external database during integration testing, or stub or mock references to the external model otherwise.

Most importantly, all my tests run really fast now.

fearless_fool
  • 33,645
  • 23
  • 135
  • 217