We have a Rails app with two databases. There’s the primary database, which is all fine and good. Normal CRUD (create/read/update/delete) life. We have a secondary database with a readonly connection. CRUD without the CUD. Just the reads.
This is all fine and good, too.
It’s easy enough to define our records to be readonly.
The username/password provided in
database.yml establishes a readonly connection at the database level.
We reinforce the database level permissions within the Ruby runtime using the
class SecondaryDatabaseRecord < ActiveRecord::Base
self.abstract_class = true
class Person < SecondaryDatabase
But what happens in tests, where we need to write test data?
On the database side, the user in
database.yml can have a read connection.
But what about the Ruby side?
One approach is to put this knowledge into the class.
With that little change, I can write
Person.create!(name: "Jake Peralta") in my tests.
I don’t like this.
Writing to a readonly database should feel surprising. The readonly record should be naive of the environment. It should just read data.
I want writing to the readonly database to stick out like a sore thumb. The code I want to write in my tests is this:
Person.create!(name: "Jake Peralta")
So instead of changing the production system, I throw this little concern in
SecondaryDatabaseRecord.concerning "MutateTestData" do
MUTATE_TEST_DATA_CALLS = 
define_method :readonly? do
This little stack allows nesting calls to
Within this eyesore of a block, mutating test data is allowed.
Outside of the block, the readonly reality is relied upon, just like production.
I like this approach to tests assuming the posture of a servant toward the production system. It reinforces the reality relied upon for stability in the real world.