Rails 5 has an irritating bug. For the past several versions, Rails handily migrates both your development and test environments when you type
bundle exec rake db:migrate. But in Rails 5, they’ve added a metadata table to each database, which sets the environment for that database. Currently, a new rails test database has its environment set to
development rather than
test, and that causes unhappiness when you attempt to run your tests, like so:
root@490add8162a0:/home# rails g model User name:string email:string invoke active_record create db/migrate/20171115223805_create_users.rb create app/models/user.rb invoke rspec create spec/models/user_spec.rb root@490add8162a0:/home# bundle exec rake db:migrate == 20171115223805 CreateUsers: migrating ============================== -- create_table(:user) -> 0.0325s == 20171115223805 CreateUser: migrated (0.0327s) ===================== root@490add8162a0:/home# bundle exec rspec rails aborted! ActiveRecord::EnvironmentMismatchError: You are attempting to modify a database that was last run in `development` environment. You are running in `test` environment. If you are sure you want to continue, first set the environment using: bin/rails db:environment:set RAILS_ENV=test /home/data/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.4/lib/active_record/tasks/database_tasks.rb:63:in `check_protected_environments!' /home/data/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.4/lib/active_record/railties/databases.rake:11:in `block (2 levels) in <top (required)>' /home/data/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.4/lib/active_record/railties/databases.rake:335:in `block (3 levels) in <top (required)>' /home/data/vendor/bundle/ruby/2.4.0/gems/railties-5.1.4/lib/rails/commands/rake/rake_command.rb:21:in `block in perform' /home/data/vendor/bundle/ruby/2.4.0/gems/railties-5.1.4/lib/rails/commands/rake/rake_command.rb:18:in `perform' /home/data/vendor/bundle/ruby/2.4.0/gems/railties-5.1.4/lib/rails/command.rb:46:in `invoke' /home/data/vendor/bundle/ruby/2.4.0/gems/railties-5.1.4/lib/rails/commands.rb:16:in `<top (required)>' bin/rails:9:in `require' bin/rails:9:in `<main>' Tasks: TOP => db:test:load => db:test:purge => db:check_protected_environments (See full trace by running task with --trace) An error occurred while loading ./spec/graphql/resolvers/create_user_spec.rb. Failure/Error: ActiveRecord::Migration.maintain_test_schema! ActiveRecord::PendingMigrationError: Migrations are pending. To resolve this issue, run: bin/rails db:migrate RAILS_ENV=test # ./spec/rails_helper.rb:27:in `<top (required)>' # ./spec/graphql/resolvers/create_user_spec.rb:1:in `require' # ./spec/graphql/resolvers/create_user_spec.rb:1:in `<top (required)>'
The error has been reported on Github Issues for Rails [https://github.com/rails/rails/issues/26731], and the fix is in place, presumably for release in Rails 5.2. But that version hasn’t been released yet, and even after it has, it may be a while before they backport it to 5.1-stable and 5.0-stable. Fortunately, there’s a teeny handy workaround:
bin/rails db:environment:set RAILS_ENV=test. Running this once from the command line (or as we’ve done, as a part of the startup script when building a docker container) means that migrate again will work.
echo '==[ rails ]===> start shotgun server and setup database' bundle exec rake db:setup bin/rails db:environment:set RAILS_ENV=test bundle exec rake start
Now your test database will have the correct environment, and you can go ahead with your migrations as normal.
Something to keep in mind, which we’ve learned while working on building a greenfield app that requires a lot of local database resetting: Don’t forget that every time you run a
rake db:setup task, you’ll need to re-run the
db:environment:set task as well. Since we assume this change won’t be backported to Rails 5.1-stable anytime soon, and since the command does no harm in the long run, we wrapped it in a rake task that enhances the existing
rake db:setup task, like so:
desc 'Enhance the db setup task to run the Rails 5 test env correction' Rake::Task['db:setup'].enhance do Rake::Task['db:correct_test_environment'].invoke end namespace :db do desc 'Start local server using Shotgun gem for automatic restarts' task :correct_test_environment do sh 'bin/rails db:environment:set RAILS_ENV=test' end end
Since Rails is open-source (and a behemoth), it can be expected that some functionality just won’t make it through to the core and get backported; being able to work around a bug without disrupting your local development workflow can be truly helpful for engineers. Also, don’t forget to talk to your Ops folks about changes you’ve made, as these might affect deploy and continuous integration.
Tagged with: database • migration • rails • rubyonrails