Last week I had to integrate one of our Rails applications at work with an additional database. While the Rails parts of the setup are quite well documented on the net, my Google Fu failed me when it came to making it work with RSpec. So here are my findings…
Integration with Rails
Adding additional databases to a Rails application is quite simple, all it takes is a database connection in database.yml and establishing the proper connection in associated models.
Let’s say we want to integrate the foo database, the additional connection might be:
1 2 3 4 5 6
To access the foo database, we conventionally use models in the Foo namespace and use a proper connection.
1 2 3
DRY it up
That’s almost it for the Rails part. At this point our application can access the foo database, but we may start wondering if we could somehow dry up database.yml, as it now contains at least three very similar entries.
The answer is merge keys. This slightly esoteric feature of YAML allows one block to include definitions from another block. In our case all our connections are identical, except for the database they use. Let’s create a block with common directives and merge it into each connection.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
That looks much cleaner, time to setup the database for our tests.
Integration with RSpec
In order to get reproducable test runs, the database used by test cases has to be in a defined state at all times. Rails ensures this property in principle by separating the test database from the development database via the Rails environment. The test database can easily be purged and should be managed by the test framework.
If we want to write tests against models using our new foo database, we should reproduce Rails’ setup and use one database in the development environment and a different one in the test environment.
Lets start with the model. The easiest way to differentiate between Rails environments is encoding it in the database connection. So we change our model to:
1 2 3
Now we have to adapt database.yml accordingly:
1 2 3 4 5 6 7 8
Having decoupled the development database from the test database, there is only one more issue: the test database is completely empty. We have to find a way to manage the schema.
Rails uses rake tasks for database management. The tasks most often used are
rake db:migrate for updating the development database and dumping its schema
rake db:test:prepare to clean and update the test database. It would be
nice if we could adapt these tasks to manage foo_test as well, so we don’t
have to remember any new tasks.
A neat feature of rake is the possibility to define tasks multiple times, in effect adding code to existing tasks. In our case, we can extend db:schema:dump and db:test:load_schema to manage the foo_test database and completely hide this fact from the developer.
Let’s put the following code into lib/tasks/db.rake:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Now, whenever we run a rake task that results in a schema dump, foo’s schema will be dumped to db/foo_schema.rb as well. Whenever we load a schema into our test database, foo_test will be updated, too.
That’s it, we are done. Time to write some specs…