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