Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading Rails Fixtures in a Specific Order when Testing

Is there a way to load Rails fixtures in a specific order while running tests? For example, take the following classes...

class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, through: :memberships
end

class Group < ActiveRecord::Base
  has_many :memberships
  has_many :users, through: :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end

Memberships have a database level foreign key constraint requiring Users and Groups to be present before they can be created. However, because Rails loads fixture alphabetically, Memberships are loaded before Users, and an error occurs stating the relationships do not exist (rightly so).

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:  insert or update on table "memberships" violates foreign key constraint

Is there a way to load the Users and Groups fixtures before loading Memberships while running tests?

like image 880
Steven Blair Avatar asked Jan 20 '15 13:01

Steven Blair


3 Answers

Your problem isn't the order that Rails is running your tests. I had the same issue as you, and after debugging for a few hours realized that ActiveRecord actually DOES disable referential integrity during insertion of fixture records to prevent these types of errors for Postgresql.

The catch is that your test database user must have superuser privileges on the test db for ActiveRecord to successfully disable referential integrity as needed for fixtures.

Here's how you fix your issue:

  1. Log into Postgresql with your default superuser (mine is "postgres")
  2. Perform the following command:

    ALTER ROLE yourtestdbuser WITH SUPERUSER;
    
  3. Enjoy your properly working fixtures.

The next release of Rails will have a warning (which I think is much needed) when a person is running a test database with Postgresql and a user without a superuser role. I found this on a Rails GitHub issue suggesting the warning.

like image 62
Chris Mapes Avatar answered Oct 04 '22 20:10

Chris Mapes


Granting PostgreSQL superuser privileges to your "test" account allows Rails to work the way it wants to work. In cases where this is not desired/feasible...

Yes, it is possible -- if not supported -- to control the order in which fixtures are loaded and deleted (both of which are important).

In test/test_helper.rb:

class ActiveRecord::FixtureSet
  class << self
    alias :orig_create_fixtures :create_fixtures
  end
  def self.create_fixtures f_dir, fs_names, *args
    # Delete all fixtures that have foreign keys, in an order that
    # doesn't break referential integrity.
    Membership.delete_all

    reset_cache

    # If we're adding any {user, group} fixtures, add them [a] in that
    # order, [b] before adding any other fixtures which might have
    # references to them.
    fs_names = %w(users groups) & fs_names | fs_names

    orig_create_fixtures f_dir, fs_names, *args
  end
end

Tested with Rails 4.2.3, and only when using fixtures :all.

like image 20
tomclegg Avatar answered Oct 04 '22 19:10

tomclegg


For, at least, Rails 5, you can replace fixtures :all with fixtures %w[users groups memberships] (in test/test_helper.rb). It will INSERT them in that order. But do not forget any fixture file :-)

like image 43
Foton Avatar answered Oct 04 '22 19:10

Foton