Tuesday, June 12, 2007

How to Migrate Fixtures in Rails

4/1/2008 - Update: check out this writeup on the fixture migrations for a more in depth analysis of the plusses and minuses of our code.

I did some work on a Rails app awhile back and we were using Cerberus for Continuous Integration. The build was in good shape. All tests passing. We turned it over to another team, but they never setup CI. Guess what happened? If you said, "The tests got stale and broken." then you are a winner. Quoth Testivus:

Tests not run waste away

Run your tests often.

Don’t let them get stale.

Rejoice when they pass.

Rejoice when they fail.


Looking at the failures it became apparent that many of the problems had been caused because of the fixtures being used. The model had been changed with migrations, but the fixtures were still old. That's easy, just change the fixtures. And I did. Yuck! Changing all the fixtures turned out to be a mind-numbing task! Consider this. Your fixtures are nothing but a representation of the data in your test database. That representation is dependent on the structure of your database. By manually changing my fixtures to use the new DB structure I had done a migration. By hand. Yuck!

I knew there was a better way, and so Jake Scruggs and I joined forces to tackle this problem. Our solution is a new rake task called db:fixtures:migrate, and you can get the code here To make it work, we have introduced the concept of a version to our test fixtures, and this makes sense because our fixtures are essentially a serialized, human readable version of the database, and in Rails the DB has a version, right? Right.

I haven't tested this task on every imaginable case, but it has worked for me on a couple of my own little projects. That is cool. Let me now bring up a side point. In learning about some of rails rake tasks I came across some thing I thought was ugly (please enlighten me if there is a reason for this). When you run tests, rails completely wipes the test db (which is good), but then instead of rebuilding the schema with migrations it just copies the schema from the development DB (which is bad). To me this is ugly, and it can cause headaches if you aren't watching out, because now your test DB depends on the state of your development DB. Two lines of code fix this, but I wanted to hear some old, wizened Rails gurus opinions on this before I went crazy with this idea. If you have a look at our rake task you can see that when we setup a test DB we use migrations to generate the schema. This is also good because if you don't run your migrations often, how do you know that they will work? You might as well run them every time you run tests. So go try it. Use it. And say what stinks.

Next up: Maybe I shouldn't use fixtures at all.

4 comments:

Bill said...

Nice work, this is exactly what I'd been hoping would exist somewhere. It worked for me almost-out-of-the-box (using Rails 2), I just had to wrap lines 14-16 in migrate_fixtures.rb in an if, so that they don't try to execute if my yaml file is empty.

Also, my fixture migration runs in its entirety every time I use it, but this isn't a big problem; it just means more waiting around. The end result is worth it.

Shlomo said...

william. Thanks for the comment. If you think your changes are worth adding to the rake task I'd be happy to take a patch. As it stands I haven't been using rails for a long time so this code has been untended for some time.

Bill said...

I dunno how useful the changes will be, cuz I don't know how much use the plugin gets. However, since any auto-generated scaffolding will be empty by default, I'd think that most who use the plugin might benefit.

In any case, I've written up a semi-comprehensive analysis of our experience using the fixture migrator on my blog at http://www.williambharding.com/blog/rails/rails-update-fixtures-upon-migration/

For anyone else who might be intending to use this, it has some handy caveats and workarounds I've discovered while this for a few months.

Thanks again for a very helpful plugin!

Shlomo said...

Wow. Nice writeup william. I'm going to modify the original post to include your link.