简体   繁体   中英

My app accesses a remote database. How do I run unit tests efficiently?

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM