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:
- Create a migration as usual
- Create a file to put your migration test in. Suggestions:
test/unit/import_legacy_devices_migration_test.rb
orspec/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')
- Migrations are (like everything in ruby), just a class. Test the
up
anddown
methods. If your logic is complex, I suggest refactoring out bits of logic to smaller methods that will be easier to test. - Before calling
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