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