views:

428

answers:

2

In a Rails application, I need a table in my database to contain constant data.

This table content is not intended to change for the moment but I do not want to put the content in the code, to be able to change it whenever needed.

I tried filling this table in the migration that created it, but this does not seem to work with the test environment and breaks my unit tests. In test environment, my model is never able to return any value while it is ok in my development environment.

Is there a way to fill that database correctly even in test environment ? Is there another way of handling these kind of data that should not be in code ?

edit

Thanks all for your answers and especially Vlad R for explaining the problem.

I now understand why my data are not loaded in test. This is because the test environment uses the db:load rake command which directly loads the schema instead of running the migrations. Having put my values in the migration only and not in the schema, these values are not loaded for test.

+3  A: 

What you are probably observing is that the test framework is not running the migrations (db:migrate), but loading db/schema.rb directly (db:load) instead.

You have two options:

  1. continue to use the migration for production and development; for the test environment, add your constant data to the corresponding yml files in db/fixtures
  2. leave the existing db/fixtures files untouched, and create another set of yml files (containing the constant data) in the same vein as db/fixtures, but usable by both test and production/development environments when doing a rake db:load schema initialization

To cover those scenarios that use db:load (instead of db:migrate - e.g. test, bringing up a new database on a new development machine using the faster db:load instead of db:migrate, etc.) is create a drop-in rakefile in RAILS_APP/lib/tasks to augment the db:load task by loading your constant intialization data from "seed" yml files (one for each model) into the database.

Use the db:seed rake task as an example. Put your seed data in db/seeds/.yml

#the command is: rake:db:load
namespace :db do
  desc 'Initialize data from YAML.'
  task :load => :environment do
    require 'active_record/fixtures'
    Dir.glob(RAILS_ROOT + '/db/seeds/*.yml').each do |file|
      Fixtures.create_fixtures('db/seeds', File.basename(file, '.*'))
    end
  end
end

To cover the incremental scenarios (db:migrate), define one migration that does the same thing as the task defined above.

If your seed data ever changes, you will need to add another migration to remove the old seed data and load the new one instead, which may be non-trivial in case of foreign-key dependencies etc.

vladr
+2  A: 

Take a look at my article on loading seed data.

There's a number of ways to do this. I like a rake task called db:populate which lets you specify your fixed data in normal ActiveRecord create statements. For getting the data into tests, I've just be loading this populate file in my test_helper. However, I think I am going to switch to a test database that already has the seed data populated.

There's also plugin called SeedFu that helps with this problem.

Whatever you do, I recommend against using fixtures for this, because they don't validate your data, so it's very easy to create invalid records.

Luke Francl