I want to test that certain conditions hold after running a migration I've written. What's the current best way to do that?
To make this concrete: I made a migration that adds a column to a model, and gives it a default value. But I forgot to update all the pre-existing instances of that model to have that default value for the new column. None of my existing tests will catch that, because they all start with a fresh database and add new data, which will have the default. But if I push to production, I know things will break, and I want my tests to tell me that.
I've found http://spin.atomicobject.com/2007/02/27/migration-testing-in-rails/, but haven't tried it. It's very old. Is that the state-of-the-art?
If you need a bash one-liner to determine whether to run a migration or not (e.g., only migrate in a Heroku release phase command when there is a pending migration), this works: (rails db:migrate:status | grep "^\s*down") && rails db:migrate || echo "No pending migrations found."
Rails Migration allows you to use Ruby to define changes to your database schema, making it possible to use a version control system to keep things synchronized with the actual code. Teams of developers − If one person makes a schema change, the other developers just need to update, and run "rake migrate".
Peter Marklund has an example gist of testing a migration here: https://gist.github.com/700194 (in rspec).
Note migrations have changed since his example to use instance methods instead of class methods.
Here's a summary:
test/unit/import_legacy_devices_migration_test.rb
or spec/migrations/import_legacy_devices_migration_spec.rb
NOTE: you probably need to explicitly load the migration file as rails will probably not load it for you. Something like this should do: require File.join(Rails.root, 'db', 'migrate', '20101110154036_import_legacy_devices')
up
and down
methods. If your logic is complex, I suggest refactoring out bits of logic to smaller methods that will be easier to test.up
, set up some some data as it would be before your migration, and assert that it's state is what you expect afterward.I hope this helps.
UPDATE: Since posting this, I posted on my blog an example migration test.
UPDATE: Here's an idea for testing migrations even after they've been run in development.
EDIT: I've updated my proof-of-concept to a full spec file using the contrived example from my blog post.
# spec/migrations/add_email_at_utc_hour_to_users_spec.rb require 'spec_helper' migration_file_name = Dir[Rails.root.join('db/migrate/*_add_email_at_utc_hour_to_users.rb')].first require migration_file_name describe AddEmailAtUtcHourToUsers do # This is clearly not very safe or pretty code, and there may be a # rails api that handles this. I am just going for a proof of concept here. def migration_has_been_run?(version) table_name = ActiveRecord::Migrator.schema_migrations_table_name query = "SELECT version FROM %s WHERE version = '%s'" % [table_name, version] ActiveRecord::Base.connection.execute(query).any? end let(:migration) { AddEmailAtUtcHourToUsers.new } before do # You could hard-code the migration number, or find it from the filename... if migration_has_been_run?('20120425063641') # If this migration has already been in our current database, run down first migration.down end end describe '#up' do before { migration.up; User.reset_column_information } it 'adds the email_at_utc_hour column' do User.columns_hash.should have_key('email_at_utc_hour') end end end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With