Friday, August 22, 2008

Populating fixtures from a database table

One problem with the add_symbolic_names code posted earlier on the blog is the need to keep the test fixtures in sync with the data in the production database — otherwise, code can reference constants that exist in production, and will break while running tests. Keeping these in sync is annoying enough that I wrote a rake task to dump the contents of a table to the appropriate fixtures file. I ended up with something like this:
      desc "Overwrite a fixture file with the contents of a table (set by TABLE=)"
      task :generate_from_table do 
        ActiveRecord::Base.establish_connection(RAILS_ENV)
        table_name = ENV['TABLE']
        File.open("test/fixtures/#{table_name}.yml", 'w') do |file|
          data = ActiveRecord::Base.connection.select_all("SELECT * FROM #{table_name}")
          rows = {}
          
          data.each do |record|
            rows["#{table_name}_#{record['id']}"] = record
          end
          
          file.write rows.to_yaml
        end
      end

Running this rake task will generate a fixture file containing all the information in the table specified by the environment variable TABLE. In the future, we’ll probably look at automating the fixture generation for these symbolic tables so we don’t have to worry about failing tests when we add new rows. UPDATE: I realized that in most cases, the symbolic name of the database row makes a much better fixture title than the id, which results in the following code:
      desc "Overwrite a fixture file with the contents of a table (set by TABLE=)"
      task :generate_from_table do 
        ActiveRecord::Base.establish_connection(RAILS_ENV)
        table_name = ENV['TABLE']
        File.open("test/fixtures/#{table_name}.yml", 'w') do |file|
          data = ActiveRecord::Base.connection.select_all("SELECT * FROM #{table_name}")
          rows = {}
          
          data.each do |record|
            fixture_name = record['symbolic_name'].blank? ? 
              "#{table_name}_#{record['id']}" : 
              record['symbolic_name'].downcase
            rows[fixture_name] = record
          end
          
          file.write rows.to_yaml
        end
      end
      
This gives us nice fixtures that look like this:
--- 
dog: 
  name: Dog
  id: "1"
  symbolic_name: DOG
cat: 
  name: Cat
  id: "2"
  symbolic_name: CAT
  

No comments: