I am a Sr. Software Developer at Oracle Cloud. The opinions expressed here are my own and not necessarily those of my employer.
Rails testing DB indexes
With ActiveRecord to create indexes we need to run migrations. But with Mongoid we simply specify indexes in our models. Here is a hypothetical User model. We want email and name to be required and email to be unique.
Instead of doing email uniqueness validation in application code it’s better to shift it to DB index via index({ email: 1 }, { unique: true })
.
Removing / creating indexes
To update our DB we need to run these commands after deploying to production.
But when we run tests it can be useful to bypass certain validations in test data setup. We could use factory girl and put required fields into our factory file. The problem is that sometimes records must belong to other records and then to create child we must first create parent just to make validation happy.
Here we are creating users w/o name or email:
The problem is it will fail to create second user because there is already a user with blank email in our DB.
So we need to manually specify email (but not necessarily name) to make MongoDB happy:
But how do we keep our test DB indexes in sync with production DB w/o manually running rake db:*
when we update indexes? If we forget it can cause situation where the tests pass but code fails in production.
rake db:*
are simply Rake tasks so what we need to is run rake from Rspec.
Now the indexes are refreshed before every test suite run.
Updating existing indexes
Another important issue to address is when we update current indexes. What if we implement multitenancy where Users belong to Clients and email uniqueness has to be w/in Client? We need to update DB index.
When we run rake db:mongoid:create_indexes
it will create new index with named email_1_client_1
vs email_1
before.
But what if the name does not change? We can switch to background indexes with index({ client: 1, email: 1 }, { unique: true, background: true })
. The best way I can think of is to manually edit index in the DB which is a little messy. In test environment we can just drop the index, collection or entire DB. Otherwise you get this error: