Peter Marklund

Peter Marklund's Home

Mon May 19 2008 08:37:37 GMT+0000 (Coordinated Universal Time)

Rails Testing Tip: Validate your Fixtures

I realized today how important it can be to validate the fixtures you use in your Test::Unit tests or RSpec specifications. I made some schema and validation changes and neglected to update all my fixtures which lead to a long and tedious debugging session. I added this RSpec specification to make sure I never have invalid fixtures again:

describe "fixtures" do
  it "should be valid" do
    ActiveRecord::Base.fixture_tables.each do |table_name|
      klass = table_name.to_s.classify.constantize
      klass.send(:find, :all).each do |object|
        puts("#{klass.name} #{object} is invalid: #{object.errors.full_messages.join(', ')}") if !object.valid?
        object.should be_valid
      end
    end
  end
end

Note: the fixtures_tables method is just a method I have defined that returns a list of all my fixture tables and I use it to set global fixtures in my test_helper.rb and spec_helper.rb files. If you are not using global fixtures, you can use this spec instead:

describe "fixtures" do
  it "should be valid" do
    Fixtures.create_fixtures(fixture_path, all_fixture_tables)
    all_fixture_tables.each do |table_name|
      begin
        klass = table_name.to_s.classify.constantize
        klass.send(:find, :all).each do |object|
          puts("#{klass.name} #{object} is invalid: #{object.errors.full_messages.join(', ')}") if !object.valid?
          object.should be_valid
        end
      rescue NameError
        # Probably a has and belongs to many mapping table with no ActiveRecord model
      end
    end
  end

  def fixture_path
    Spec::Runner.configuration.fixture_path
  end

  def all_fixture_tables
    Dir[File.join(fixture_path, "*.yml")].map { |file| File.basename(file[/^(.+)\.[^.]+?$/, 1]) }    
  end
end

I think it would be nice if Rails/RSpec has fixture validation built in and turned on by default.