Merge db:migrate + db:test:prepare

I had a crazy idea today based off a reader’s question[1].

Why are rake db:migrate and rake db:test:prepare two different tasks? Why can’t we have rake db:migrate migrate both the development and the test database at the same time? So many noobs (I’ll include myself in that basket too) get tripped up on this. How many times have you ran into it?

From what I can see, there is no reason at all why the development and test databases should be different. What would you think about having these as one task in Rails 3.0.1 or Rails 3.1, with obviously a deprecation warning for rake db:test:prepare or something?

[1] http://www.manning-sandbox.com/thread.jspa?threadID=39618&tstart=0

I had a crazy idea today based off a reader's question[1]. Why are rake db:migrate and rake db:test:prepare two different tasks? Why can't we have rake db:migrate migrate both the development and the test database at the same time? So many noobs (I'll include myself in that basket too) get tripped up on this. How many times have you ran into it? From what I can see, there is no reason at all why the development and test databases should be different. What would you think about having these as one task in Rails 3.0.1 or Rails 3.1, with obviously a deprecation warning for rake db:test:prepare or something?

Either that or dropping/loading the schema every time you require "test_helper" (or equivalent), but definitely +1

One could take that idea a step further. Since many popular databases don't support transactional DDL, it's not hard to write a bad migration which fails and leaves the database stuck between migrations, requiring manual cleanup. If we did as you suggest, we could try to apply the migration first to the test database and only if it succeeded, try to apply it to the development database. If it failed, blowing away and recreating the test database would almost certainly be less painful than manually cleaning up development.

- donald

I love this idea of keeping the test database as a backup and rolling back to it. I can completely understand why development and test are two separate databases and this would solidify that even further.

However, I can see a catch. If the test database is running then wouldn’t the data be wiped? Perhaps you would require this data for the development environment and if it’s gone, how are you going to get it back?

Luckily there’s also a way around this. Any important development data should be easily recoverable by running rake db:seed, because you’ve got your important data in db/seeds.rb, right?

Good to see there’s some support behind this :slight_smile:

This problem arises when you run the suite and there are pending migrations.

So you are about to run tests, and the test tasks migrate... which environment? How do you know you are not in staging?

This dual-migration should only happen if you’re in the development environment. If you’re in another environment such as staging it should only run it for the current environment.

alternately, rather than change the behavior of the current rake tasks, it could be done by a separate task

Such as rake db:migrate:all? I like this idea too.

Such as rake db:migrate:all? I like this idea too.

IMHO rake db:migrate:local (simply means development and test) is a better name. rake db:migrate:all gives an impression that it will run migrations for all environments.

andhapp

But how can you tell from within the "test" environment?

In that case then, rake db:migrate:all (or rake db:migrate:local) seems like a decent task name to run migrations on development+test only, no?

nothing intrigues me more than db:test:clone

I personally have never used rake db:test:prepare. I generally use rake db:migrate RAILS_ENV=test. Something more concise is welcome.

Allen Madsen http://www.allenmadsen.com

Not sure I follow.

You do not need an extra task to run migrations in dev and test. Because the test database is loaded straight from db/schema.rb.

The flow is: you run db:migrate in the environment you want, say "development". That dumps db/schema.rb as a side-effect, and the test tasks load that file. They do not use migrations.

Some test tools such as Cucumber (and I’m sure I’ve seen it in RSpec but I cannot reproduce it right now) do not run the migrations before they run the tests. I think Rails should automatically do it so that when you run the tests the database is already setup. Why run it every time when you should only run it once? This would speed up the execution of the tests.

If I understand you right, you’re saying rake db:test:prepare is synonymous with rake db:schema:load RAILS_ENV=test instead of migrate.

Allen Madsen http://www.allenmadsen.com

Bingo. That’s all rake db:test:prepare does: loads the schema into the test database. Whereas rake db:test:clone clones the structure+data from the development database to the test database.

When the test tasks warn that there are pending migrations they are not saying that you need to run migrations on the test schema. They are saying that db/schema.rb is outdated. And halt because normally that's not good.

Now, from within the "test" environment you do not have information to update db/schema.rb automatically.

The task to fix that already exists, it is db:migrate.

Perhaps a better error message?

There may be valid reasons to not have your database on the most recent migration. It would be difficult for rails to know when it should go to the newest version and when it should stay at a particular one.

Allen Madsen http://www.allenmadsen.com

Some test tools such as Cucumber (and I'm sure I've seen it in RSpec but I cannot reproduce it right now) do not run the migrations before they run the tests. I think Rails should automatically do it so that when you run the tests the database is already setup. Why run it every time when you should only run it once? This would speed up the execution of the tests.

What happens is that rails', cucumber's and rspec's rake tasks run db:test:prepare before. But if you run a single test, like "ruby test/unit/model_test.rb", then the test database isn't loaded.

Maybe a simple solution for all this is providing a "rails/test/reset_database" that, when required, drops the database and requires Rails.root.join("db/schema"). That way we can include that in rails/test_help and everytime the file is loaded, whether from rake or an isolated test case being run, the database is up-to-date.

Thoughts?

-foca